Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of InvisibilityPotion v0.4.1
plugins/InvisibilityPotion.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using InvisibilityPotion.Commands; using InvisibilityPotion.Config; using InvisibilityPotion.Effects; using InvisibilityPotion.Goggles; using InvisibilityPotion.Items; using InvisibilityPotion.Net; using InvisibilityPotion.Plants; using InvisibilityPotion.Visuals; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Splatform; using UnityEngine; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("InvisibilityPotion")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.4.1.0")] [assembly: AssemblyInformationalVersion("0.4.1+7581b4c4750816ffce77b850b96f8b62f61851e2")] [assembly: AssemblyProduct("InvisibilityPotion")] [assembly: AssemblyTitle("InvisibilityPotion")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.4.1.0")] namespace InvisibilityPotion { public static class PatchHealth { public static string TargetKey(string typeName, string methodName) { return typeName + "." + methodName; } public static IReadOnlyList<string> MissingTargets(IEnumerable<string> expected, IEnumerable<string> patched) { HashSet<string> patchedSet = new HashSet<string>(patched); return expected.Where((string t) => !patchedSet.Contains(t)).ToList(); } public static IReadOnlyList<string> PatchedTargets(Harmony harmony) { return (from m in harmony.GetPatchedMethods().Where(delegate(MethodBase m) { Patches patchInfo = Harmony.GetPatchInfo(m); return patchInfo != null && patchInfo.Owners.Contains(harmony.Id); }) select TargetKey(m.DeclaringType?.Name ?? "?", m.Name)).Distinct().ToList(); } public static IReadOnlyList<string> Report(Harmony harmony, IEnumerable<string> expectedTargets) { IReadOnlyList<string> readOnlyList = PatchedTargets(harmony); IReadOnlyList<string> readOnlyList2 = MissingTargets(expectedTargets, readOnlyList); foreach (string item in readOnlyList2) { Plugin.Log.LogError((object)("Patch missing: " + item + ". The game may have changed; this feature is disabled.")); } Plugin.Log.LogInfo((object)$"Patch health: {readOnlyList.Count} targets patched, {readOnlyList2.Count} missing"); return readOnlyList2; } } [BepInPlugin("saikopsych.InvisibilityPotion", "InvisibilityPotion", "0.4.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "saikopsych.InvisibilityPotion"; public const string PluginName = "InvisibilityPotion"; public const string PluginVersion = "0.4.1"; public static readonly List<string> ExpectedPatchTargets = new List<string> { PatchHealth.TargetKey("BaseAI", "CanHearTarget"), PatchHealth.TargetKey("BaseAI", "CanSeeTarget"), PatchHealth.TargetKey("BaseAI", "FindEnemy"), PatchHealth.TargetKey("MonsterAI", "UpdateTarget"), PatchHealth.TargetKey("MonsterAI", "UpdateSleep"), PatchHealth.TargetKey("Player", "UpdateStealth"), PatchHealth.TargetKey("Character", "Damage"), PatchHealth.TargetKey("Attack", "StartDraw"), PatchHealth.TargetKey("Humanoid", "StartAttack"), PatchHealth.TargetKey("Humanoid", "BlockAttack"), PatchHealth.TargetKey("VisEquipment", "UpdateLodgroup"), PatchHealth.TargetKey("VisEquipment", "SetChestEquipped"), PatchHealth.TargetKey("VisEquipment", "SetLegEquipped"), PatchHealth.TargetKey("Player", "CanConsumeItem"), PatchHealth.TargetKey("EnemyHud", "TestShow"), PatchHealth.TargetKey("ZNet", "UpdatePlayerList"), PatchHealth.TargetKey("ZDOMan", "SendZDOs"), PatchHealth.TargetKey("Player", "UpdatePlacementGhost"), PatchHealth.TargetKey("Player", "GetMaxCarryWeight"), PatchHealth.TargetKey("Player", "PlacePiece"), PatchHealth.TargetKey("Player", "CheckRun"), PatchHealth.TargetKey("Plant", "Grow"), PatchHealth.TargetKey("ZoneSystem", "CreateGhostZones") }; public static ManualLogSource Log { get; private set; } public static Harmony HarmonyInstance { get; private set; } public static IReadOnlyList<string> MissingPatches { get; private set; } = new List<string>(); public static void Verbose(string message) { if (PluginConfig.VerboseLogging) { Log.LogInfo((object)message); } else { Log.LogDebug((object)message); } } private void Awake() { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = true; try { PluginConfig.Bind(((BaseUnityPlugin)this).Config); } catch (Exception arg) { Log.LogError((object)$"Config binding failed: {arg}"); } try { Localization.Register(); } catch (Exception arg2) { Log.LogError((object)$"Localization registration failed: {arg2}"); } try { StatusEffects.Register(); } catch (Exception arg3) { Log.LogError((object)$"Status effect registration failed: {arg3}"); } try { AssetBundles.Load(); } catch (Exception arg4) { Log.LogError((object)$"Asset bundle load failed: {arg4}"); } SynchronizationManager.OnConfigurationSynchronized += delegate { PluginConfig.Refresh(); Cultivation.ApplyGrowTime(); }; PrefabManager.OnVanillaPrefabsAvailable += RegisterItemsOnce; HarmonyInstance = new Harmony("saikopsych.InvisibilityPotion"); Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(typeof(Plugin).Assembly); foreach (Type type in typesFromAssembly) { try { HarmonyInstance.CreateClassProcessor(type).Patch(); } catch (Exception ex) { Log.LogError((object)("Failed to apply patches in " + type.FullName + ": " + ex.Message)); } } MissingPatches = PatchHealth.Report(HarmonyInstance, ExpectedPatchTargets); try { ((Component)this).gameObject.AddComponent<VeilController>(); } catch (Exception arg5) { Log.LogError((object)$"Veil controller setup failed: {arg5}"); } try { ((Component)this).gameObject.AddComponent<VeilSightDriver>(); } catch (Exception arg6) { Log.LogError((object)$"Veil sight driver setup failed: {arg6}"); } try { AdminCommands.Register(); } catch (Exception arg7) { Log.LogError((object)$"Admin command registration failed: {arg7}"); } Log.LogInfo((object)"InvisibilityPotion 0.4.1 loaded"); } private void RegisterItemsOnce() { PrefabManager.OnVanillaPrefabsAvailable -= RegisterItemsOnce; try { PotionItems.Register(); } catch (Exception arg) { Log.LogError((object)$"Item registration failed: {arg}"); } try { ModelItems.Register(); } catch (Exception arg2) { Log.LogError((object)$"Model registration failed: {arg2}"); } } private void OnDestroy() { Harmony harmonyInstance = HarmonyInstance; if (harmonyInstance != null) { harmonyInstance.UnpatchSelf(); } } } } namespace InvisibilityPotion.Visuals { public sealed class FogAnchor { public string Name = ""; public bool Enabled = true; public float Radius = 0.15f; public float X; public float Y; public float Z; public FogAnchor Clone() { return (FogAnchor)MemberwiseClone(); } public string Format() { return (Enabled ? "on" : "off") + "," + FloatList.Format(Radius) + "," + FloatList.Format(X) + "," + FloatList.Format(Y) + "," + FloatList.Format(Z); } public static bool TryParse(string name, string text, out FogAnchor anchor) { anchor = null; if (string.IsNullOrWhiteSpace(text)) { return false; } string[] array = text.Split(new char[1] { ',' }); if (array.Length != 5) { return false; } if (!TryParseSwitch(array[0], out var on)) { return false; } float[] array2 = new float[4]; for (int i = 0; i < 4; i++) { if (!FloatList.TryParseOne(array[i + 1], out array2[i])) { return false; } } if (array2[0] < 0f) { return false; } anchor = new FogAnchor { Name = name, Enabled = on, Radius = array2[0], X = array2[1], Y = array2[2], Z = array2[3] }; return true; } public static bool TryParseSwitch(string text, out bool on) { switch ((text ?? "").Trim().ToLowerInvariant()) { case "on": case "true": case "1": on = true; return true; case "off": case "false": case "0": on = false; return true; default: on = false; return false; } } } public enum FogAlphaMode { Both, Material, Vertex } public struct FogRgb { public float R; public float G; public float B; public FogRgb(float r, float g, float b) { R = r; G = g; B = b; } public static FogRgb Blend(FogRgb a, FogRgb b, float t) { t = ((t < 0f) ? 0f : ((t > 1f) ? 1f : t)); return new FogRgb(a.R + (b.R - a.R) * t, a.G + (b.G - a.G) * t, a.B + (b.B - a.B) * t); } } public enum FogEmitterMode { Bones, Mesh } public enum FogTrailMode { Follow, Trail } public enum FogValueKind { Float, Bool, Text } public enum FogOuterShape { Volume, Ring } public enum FogLayerKind { Inner, Outer, Ground } public static class LookDefaults { public const int Revision = 14; public const int FullResetRevision = 14; public const int SoftFogRevision = 5; public const int OuterRingRevision = 6; public const string OuterKeyPrefix = "Outer"; public const int Tier1BodyOffRevision = 3; public static readonly int[] ResetTiers = new int[2] { 1, 2 }; public static readonly string[] BodyVeilModes = new string[4] { null, "Off", "Distortion", "Distortion" }; public static bool ResetsOuterKey(int tier, string key, int fromRevision) { if (tier == 2 && fromRevision < 6 && key != null) { return key.StartsWith("Outer", StringComparison.Ordinal); } return false; } public static bool ResetsTiers(int fromRevision) { return fromRevision < 14; } public static string MigrateFogMaterial(string current, int fromRevision) { if (fromRevision >= 5 || !(FogSettings.NormalizeFogMaterial(current) == "swamp_mist")) { return current; } return "soft"; } public static string MigrateBodyVeilMode(int tier, string current, int fromRevision) { if (tier != 1 || fromRevision >= 3 || !string.Equals((current ?? "").Trim(), "Distortion", StringComparison.Ordinal)) { return current; } return "Off"; } } public sealed class FogKey { public readonly string Name; public readonly FogValueKind Kind; public readonly string Description; public FogKey(string name, FogValueKind kind, string description) { Name = name; Kind = kind; Description = description; } } public sealed class FogSettings { public const int ParticleWarnThreshold = 200; public const int ParticleHardCap = 300; public const float OuterRingThickness = 0.25f; public const float OuterVolumeSizeMinFactor = 0.8f; public const float OuterVolumeSizeMaxFactor = 1.2f; public const float OuterVolumeSpeed = 0.05f; public const float OuterVolumeFadeIn = 0.15f; public const float OuterVolumeFadeOut = 0.35f; public const float OuterVolumeGrow = 1.35f; public const float OuterVolumeSpinDegPerSecond = 4f; public const float OuterVolumeLifetimeMeanAlpha = 0.75f; public const float OuterVolumeMeanGrow = 1.175f; public const float OuterMaxColumnOpacity = 0.5f; public const int InnerParticleCap = 24; public const int OuterVolumeParticleCap = 200; public const float OuterBurstRemainingMinFactor = 0.3f; public const float OuterBurstRemainingMaxFactor = 0.85f; public const float OuterFollowRadiusFactor = 0.7f; public const float OuterFollowBurstFraction = 0.6f; public const float OuterFollowSizeFactor = 0.85f; public const float OuterFollowTangentialSpeed = 0.15f; public const float OuterFollowOrbitRadiusFactor = 0.65f; public const float MaxOuterRotation = 180f; public static readonly string[] DefaultOuterAnchors = new string[3] { "Head", "Chest", "Hips" }; public static readonly string[] AnchorNames = new string[13] { "Head", "Chest", "Hips", "LeftShoulder", "RightShoulder", "LeftHand", "RightHand", "LeftUpperLeg", "RightUpperLeg", "LeftLowerLeg", "RightLowerLeg", "LeftFoot", "RightFoot" }; public bool Enabled = true; public float Rate = 4f; public float Size = 1.5f; public float Lifetime = 5f; public float Speed = 0.05f; public float Alpha = 0.5f; public float R = 0.85f; public float G = 0.87f; public float B = 0.9f; public bool DynamicColor; public float Emission; public float SpreadX = 1f; public float SpreadY = 0.35f; public float SpreadZ = 1f; public float Drift; public bool Trail; public float MeshRate; public bool OuterEnabled; public float OuterRadius = 1.4f; public float OuterAlpha = 0.35f; public float OuterRate = 14f; public float OuterRateDistance; public float OuterSize = 1.8f; public float OuterLifetime = 3f; public float OuterSpreadY = 0.35f; public float OuterHeightSigma = 0.9f; public float OuterBurst; public float OuterFollowShare = 0.4f; public bool OuterTrail; public float OuterRotation; public float OuterOffsetY; public bool OuterHorizontal = true; public FogOuterShape OuterShape; public List<string> OuterAnchors = new List<string>(DefaultOuterAnchors); public bool GroundEnabled; public float GroundRate = 4f; public float GroundRateDistance = 2f; public float GroundSize = 0.8f; public float GroundGrow = 3f; public float GroundLifetime = 7f; public float GroundAlpha = 0.22f; public float GroundRadius = 0.6f; public float GroundHeight = 0.15f; public float GroundDrift = 0.05f; public float DistortionStrength = 0.1f; public float DR = 1f; public float DG = 1f; public float DB = 1f; public float DA = 0.08f; public float DistortionWave = -1f; public string FogMaterial = "soft"; public FogEmitterMode EmitterMode; public float MeshOffset = 0.03f; public List<FogAnchor> Anchors = new List<FogAnchor>(); public const string DefaultFogMaterial = "soft"; public const string SoftFogBase = "swamp_mist"; public static readonly string[] FogMaterialNames = new string[5] { "soft", "swamp_mist", "ghost_smoke", "wraith_smoke", "slowwispysmoke" }; public static readonly string[] ObsoleteKeys = new string[5] { "OuterRadiusMultiplier", "OuterAlphaFactor", "OuterRateFactor", "OuterSizeFactor", "OuterLifetimeFactor" }; public static readonly string[] GroundKeys = new string[10] { "GroundEnabled", "GroundRate", "GroundRateDistance", "GroundSize", "GroundGrow", "GroundLifetime", "GroundAlpha", "GroundRadius", "GroundHeight", "GroundDrift" }; public const float GroundBudgetSpeed = 7f; public const float GroundSizeMinFactor = 0.7f; public const float GroundSizeMaxFactor = 1.3f; public const float GroundUpperOffset = 0.3f; public const float GroundUpperRateFactor = 0.5f; public const float GroundUpperAlphaFactor = 0.7f; public const int GroundParticleBudget = 60; public const string AnchorPrefix = "Anchor."; public const string Readme = "How the fog of a hidden player is built (each tier has its own [Fog.TierN] section):\nBody cloud (keys without a prefix: Rate, Size, Alpha, ...): fog sitting directly on the body. It moves with you and hides your outline.\nWide fog (Outer* keys): large, soft, light fog puffs spreading far around you (7 to 9 metres) like the game's own mist patches, thickest near the ground and close to you, slowly dissolving upwards and outwards. OuterBurst creates it the moment the effect starts; part of it moves with you (OuterFollowShare), the rest stays behind as a trail.\nGround fog (Ground* keys): flat fog patches at your feet. They stay where they appeared and spread, so your path fills with low fog.\nAlpha keys say how visible a layer is (0 = invisible, 1 = solid); Rate keys how many puffs appear; Lifetime keys how long a puff lingers."; public static readonly IReadOnlyList<FogKey> Keys = BuildKeys(); public FogRgb Color => new FogRgb(R, G, B); public FogTrailMode InnerTrailMode { get { if (!Trail) { return FogTrailMode.Follow; } return FogTrailMode.Trail; } } public FogTrailMode OuterTrailMode { get { if (!OuterTrail) { return FogTrailMode.Follow; } return FogTrailMode.Trail; } } public float OuterOrbitalRadPerSecond => OuterRotation * (float)Math.PI / 180f; public bool InnerActive { get { if (Alpha > 0f) { if (!(Rate > 0f)) { if (EmitterMode == FogEmitterMode.Mesh) { return MeshRate > 0f; } return false; } return true; } return false; } } public bool OuterActive { get { if (OuterEnabled && OuterRate > 0f && OuterAlpha > 0f) { return OuterAnchors.Count > 0; } return false; } } public bool GroundActive { get { if (GroundEnabled && GroundAlpha > 0f) { if (!(GroundRate > 0f)) { return GroundRateDistance > 0f; } return true; } return false; } } public float GroundBudgetRate => Math.Max(0f, GroundRate) + Math.Max(0f, GroundRateDistance) * 7f; public float LiveParticlesGround { get { if (!GroundEnabled) { return 0f; } return GroundBudgetRate * Math.Max(0f, GroundLifetime); } } public int GroundMaxParticles => MaxParticles(GroundBudgetRate, GroundLifetime); public float GroundUpperHeight => GroundHeight + 0.3f; public float GroundUpperAlpha => GroundAlpha * 0.7f; public float GroundUpperRate => Math.Max(0f, GroundRate) * 0.5f; public float GroundUpperRateDistance => Math.Max(0f, GroundRateDistance) * 0.5f; private int GroundTotalMaxParticles { get { float num = GroundBudgetRate * 1.5f * Math.Max(0f, GroundLifetime) * 1.3f + 12f; return (int)Math.Min(60.0, Math.Max(12.0, Math.Ceiling(num))); } } public int GroundLowerMaxParticles { get { int groundTotalMaxParticles = GroundTotalMaxParticles; int val = (int)Math.Round((float)groundTotalMaxParticles / 1.5f); return Math.Min(groundTotalMaxParticles - 4, Math.Max(8, val)); } } public int GroundUpperMaxParticles => GroundTotalMaxParticles - GroundLowerMaxParticles; public float LiveParticlesInner => Math.Max(0f, Rate) * Math.Max(0f, Lifetime); public float LiveParticlesOuter { get { if (!OuterEnabled) { return 0f; } return Math.Max(0f, OuterRate) * Math.Max(0f, OuterLifetime); } } public float LiveParticlesMesh { get { int num = 0; foreach (FogAnchor anchor in Anchors) { if (anchor.Enabled) { num++; } } return MeshEmitterRate(num) * Math.Max(0f, Lifetime); } } public bool ExceedsParticleBudget { get { if (!(((EmitterMode == FogEmitterMode.Mesh) ? LiveParticlesMesh : LiveParticlesInner) > 200f) && !(LiveParticlesOuter > 200f)) { return LiveParticlesGround > 200f; } return true; } } public int InnerMaxParticles => Math.Min(24, MaxParticles(Rate, Lifetime)); public float OuterEffectiveRateDistance { get { if (!OuterTrail) { return 0f; } return Math.Max(0f, OuterRateDistance); } } public int OuterMaxParticles => OuterFollowMaxParticles + OuterTrailMaxParticles; public float OuterFollowShareEffective { get { if (OuterShape != FogOuterShape.Volume || !OuterTrail) { return 0f; } return Clamp01(OuterFollowShare); } } public bool OuterHasFollow { get { if (OuterActive) { return OuterFollowShareEffective > 0f; } return false; } } public bool OuterHasTrail { get { if (OuterActive) { return OuterFollowShareEffective < 1f; } return false; } } public float OuterFollowRate => Math.Max(0f, OuterRate) * OuterFollowShareEffective; public float OuterTrailRate => Math.Max(0f, OuterRate) * (1f - OuterFollowShareEffective); public float OuterFollowRadius => OuterRadius * 0.7f; public float OuterFollowSize => OuterSize * 0.85f; public float OuterFollowOrbitalRadPerSecond { get { float num = OuterFollowRadius * 0.65f; return OuterOrbitalRadPerSecond + ((num > 0.0001f) ? (0.15f / num) : 0f); } } private int OuterFollowCap { get { float outerFollowShareEffective = OuterFollowShareEffective; if (!(outerFollowShareEffective > 0f)) { return 0; } return Math.Max(1, (int)Math.Round(200f * outerFollowShareEffective, MidpointRounding.AwayFromZero)); } } public int OuterFollowMaxParticles { get { if (!(OuterFollowShareEffective > 0f)) { return 0; } return Math.Min(OuterFollowCap, MaxParticles(OuterFollowRate, OuterLifetime) + OuterFollowBurstShare); } } public int OuterTrailMaxParticles { get { if (OuterShape != FogOuterShape.Volume) { return MaxParticles(OuterRate, OuterLifetime); } if (OuterFollowShareEffective >= 1f) { return 0; } return Math.Min(200 - OuterFollowCap, MaxParticles(OuterTrailRate + OuterEffectiveRateDistance * 7f, OuterLifetime)); } } private int OuterBurstTotal { get { if (!OuterActive || OuterShape != FogOuterShape.Volume) { return 0; } return (int)Math.Round(Math.Max(0f, OuterBurst), MidpointRounding.AwayFromZero); } } public float OuterFollowBurstFractionEffective { get { float outerFollowShareEffective = OuterFollowShareEffective; if (!(outerFollowShareEffective <= 0f)) { if (!(outerFollowShareEffective >= 1f)) { return 0.6f; } return 1f; } return 0f; } } private int OuterFollowBurstShare => (int)Math.Round((float)OuterBurstTotal * OuterFollowBurstFractionEffective, MidpointRounding.AwayFromZero); public int OuterFollowBurstCount => Math.Min(OuterFollowMaxParticles, OuterFollowBurstShare); public int OuterTrailBurstCount => Math.Min(OuterTrailMaxParticles, OuterBurstTotal - OuterFollowBurstShare); public int OuterBurstCount => OuterFollowBurstCount + OuterTrailBurstCount; public float OuterCentreOverlap { get { if (!OuterActive || OuterShape != FogOuterShape.Volume) { return 0f; } float num = Math.Max(0f, OuterLifetime); float num2 = 0f; if (OuterHasFollow) { num2 += VolumeCentreOverlap(Math.Min(OuterFollowMaxParticles, OuterFollowRate * num + (float)OuterFollowBurstCount), OuterFollowRadius, OuterFollowSize); } if (OuterHasTrail) { num2 += VolumeCentreOverlap(Math.Min(OuterTrailMaxParticles, OuterTrailRate * num + (float)OuterTrailBurstCount), OuterRadius, OuterSize); } return num2; } } public FogOverlapCap OuterOverlapCap { get { float outerCentreOverlap = OuterCentreOverlap; if (!(outerCentreOverlap > 0f)) { return default(FogOverlapCap); } return new FogOverlapCap(outerCentreOverlap, OuterAlpha * 0.75f * FogSprite.MeanDiscAlpha, 0.5f); } } public static void OuterVolumeDrift(float uAngle, out float x, out float z) { OuterVolumeDrift(uAngle, 0.05f, out x, out z); } public static void OuterVolumeDrift(float uAngle, float speed, out float x, out float z) { double num = (double)uAngle * 2.0 * Math.PI; x = (float)(Math.Cos(num) * (double)speed); z = (float)(Math.Sin(num) * (double)speed); } public static string NormalizeFogMaterial(string text) { string b = (text ?? "").Trim(); string[] fogMaterialNames = FogMaterialNames; foreach (string text2 in fogMaterialNames) { if (string.Equals(text2, b, StringComparison.OrdinalIgnoreCase)) { return text2; } } return null; } public static bool TryParseEmitterMode(string text, out FogEmitterMode mode) { if (Enum.TryParse<FogEmitterMode>((text ?? "").Trim(), ignoreCase: true, out mode)) { return Enum.IsDefined(typeof(FogEmitterMode), mode); } return false; } public static bool TryParseOuterShape(string text, out FogOuterShape shape) { shape = FogOuterShape.Volume; string b = (text ?? "").Trim(); foreach (FogOuterShape value in Enum.GetValues(typeof(FogOuterShape))) { if (string.Equals(value.ToString(), b, StringComparison.OrdinalIgnoreCase)) { shape = value; return true; } } return false; } private static void ApplyFogVolume(FogSettings s, float alpha, float radius, float heightSigma, float size, float lifetime, float rate, float rateDistance, float burst) { s.OuterEnabled = true; s.OuterShape = FogOuterShape.Volume; s.OuterAnchors = new List<string> { "Hips" }; s.OuterHorizontal = false; s.OuterSize = size; s.OuterAlpha = alpha; s.OuterRadius = radius; s.OuterSpreadY = 0.35f; s.OuterHeightSigma = heightSigma; s.OuterRate = rate; s.OuterRateDistance = rateDistance; s.OuterLifetime = lifetime; s.OuterRotation = 0f; s.OuterOffsetY = 0f; s.OuterTrail = true; s.OuterBurst = burst; s.OuterFollowShare = 0.4f; } private static void ApplyHazeColor(FogSettings s) { s.R = 0.55f; s.G = 0.57f; s.B = 0.6f; } public static FogSettings Defaults(int tier) { FogSettings fogSettings = new FogSettings(); string[] anchorNames = AnchorNames; foreach (string name in anchorNames) { fogSettings.Anchors.Add(DefaultAnchor(name)); } switch (tier) { case 1: fogSettings.Rate = 7f; fogSettings.Size = 1.1f; fogSettings.Lifetime = 2.4f; fogSettings.Speed = 0.03f; fogSettings.Alpha = 0.12f; ApplyHazeColor(fogSettings); fogSettings.Emission = 0f; fogSettings.SpreadX = 1f; fogSettings.SpreadY = 0.6f; fogSettings.SpreadZ = 1f; fogSettings.Drift = 0.03f; fogSettings.Trail = false; ApplyFogVolume(fogSettings, 0.3f, 7f, 0.8f, 4.5f, 12f, 8f, 3f, 60f); fogSettings.GroundEnabled = true; fogSettings.GroundAlpha = 0.14f; fogSettings.DistortionStrength = 0.04f; fogSettings.DA = 0.5f; break; case 2: fogSettings.Rate = 9f; fogSettings.Size = 1.2f; fogSettings.Lifetime = 2.6f; fogSettings.Speed = 0.05f; fogSettings.Alpha = 0.14f; ApplyHazeColor(fogSettings); fogSettings.Emission = 0f; fogSettings.SpreadX = 1.125f; fogSettings.SpreadY = 0.6f; fogSettings.SpreadZ = 1.025f; fogSettings.Drift = -0.066f; ApplyFogVolume(fogSettings, 0.4f, 9f, 1f, 5.5f, 14f, 10f, 4f, 80f); fogSettings.GroundEnabled = true; fogSettings.GroundRate = 6f; fogSettings.GroundRateDistance = 3f; fogSettings.GroundSize = 1.2f; fogSettings.GroundGrow = 2.5f; fogSettings.GroundAlpha = 0.16f; fogSettings.GroundRadius = 0.9f; fogSettings.DistortionStrength = 0.1f; fogSettings.DA = 0.08f; break; case 3: fogSettings.Enabled = false; fogSettings.DistortionStrength = 0.03f; fogSettings.DA = 0.02f; break; } return fogSettings; } public static FogAnchor DefaultAnchor(string name) { switch (name) { case "Head": return new FogAnchor { Name = name, Radius = 0.15f, Y = 0.05f }; case "Chest": return new FogAnchor { Name = name, Radius = 0.25f }; case "Hips": return new FogAnchor { Name = name, Radius = 0.22f }; case "LeftShoulder": case "RightShoulder": return new FogAnchor { Name = name, Radius = 0.12f }; case "LeftHand": case "RightHand": return new FogAnchor { Name = name, Radius = 0.12f }; case "LeftUpperLeg": case "RightUpperLeg": return new FogAnchor { Name = name, Radius = 0.12f, Y = -0.22f }; case "LeftLowerLeg": case "RightLowerLeg": return new FogAnchor { Name = name, Radius = 0.12f, Y = -0.2f }; case "LeftFoot": case "RightFoot": return new FogAnchor { Name = name, Radius = 0.12f, Y = 0.05f }; default: return new FogAnchor { Name = name }; } } public FogSettings Clone() { FogSettings fogSettings = (FogSettings)MemberwiseClone(); fogSettings.Anchors = new List<FogAnchor>(Anchors.Count); foreach (FogAnchor anchor in Anchors) { fogSettings.Anchors.Add(anchor.Clone()); } fogSettings.OuterAnchors = new List<string>(OuterAnchors); return fogSettings; } public FogAnchor Anchor(string name) { foreach (FogAnchor anchor in Anchors) { if (string.Equals(anchor.Name, name, StringComparison.OrdinalIgnoreCase)) { return anchor; } } return null; } public float MeshEmitterRate(int enabledAnchors) { if (!(MeshRate > 0f)) { return Math.Max(0f, Rate) * (float)Math.Max(0, enabledAnchors); } return MeshRate; } public int MeshMaxParticles(int enabledAnchors) { return Math.Min(Math.Max(8, 24 * Math.Max(0, enabledAnchors)), MaxParticles(MeshEmitterRate(enabledAnchors), Lifetime)); } public static float VolumeCentreOverlap(float live, float radius, float size) { if (!(radius > 0f) || !(live > 0f) || !(size > 0f)) { return 0f; } float num = 0.5f * size * 0.8f * 1.175f; return live * (float)FogVolumeShape.RadiusShare(num / radius); } public static float OuterBurstRemainingLifetime(float startLifetime, float random01) { float num = ((random01 < 0f) ? 0f : ((random01 > 1f) ? 1f : random01)); return startLifetime * (0.3f + 0.55f * num); } public int MaxParticles(float rate) { return MaxParticles(rate, Lifetime); } public static int MaxParticles(float rate, float lifetime) { float num = Math.Max(0f, rate) * Math.Max(0f, lifetime) * 1.3f + 4f; return (int)Math.Min(300.0, Math.Max(8.0, Math.Ceiling(num))); } public static bool TryParseAnchorList(string text, out List<string> names) { names = new List<string>(); if (string.IsNullOrWhiteSpace(text)) { return true; } string[] array = text.Split(new char[1] { ',' }); foreach (string text2 in array) { string text3 = text2.Trim(); if (text3.Length == 0) { continue; } string text4 = null; string[] anchorNames = AnchorNames; foreach (string text5 in anchorNames) { if (string.Equals(text5, text3, StringComparison.OrdinalIgnoreCase)) { text4 = text5; break; } } if (text4 == null) { names = null; return false; } if (!names.Contains(text4)) { names.Add(text4); } } return true; } public static bool TryParseAlphaMode(string text, out FogAlphaMode mode) { if (Enum.TryParse<FogAlphaMode>((text ?? "").Trim(), ignoreCase: true, out mode)) { return Enum.IsDefined(typeof(FogAlphaMode), mode); } return false; } private static List<FogKey> BuildKeys() { List<FogKey> list = new List<FogKey> { new FogKey("Enabled", FogValueKind.Bool, "Body cloud on or off: the fog that sits on the hidden player's body"), new FogKey("Rate", FogValueKind.Float, "Body cloud: new puffs per second on each body part; higher = a thicker cloud"), new FogKey("Size", FogValueKind.Float, "Body cloud: size of one puff in metres (each puff is 0.6x to 1.25x of this)"), new FogKey("Lifetime", FogValueKind.Float, "Body cloud: seconds a puff lingers before it fades; higher = a thicker, slower cloud"), new FogKey("Speed", FogValueKind.Float, "Body cloud: how fast a new puff drifts away from the body, in metres per second"), new FogKey("Alpha", FogValueKind.Float, "How visible the body cloud is (0 = invisible, 1 = solid)"), new FogKey("Color", FogValueKind.Text, "Colour of all fog of this tier as r,g,b (each 0..1; 0.55,0.57,0.6 = mid grey)"), new FogKey("DynamicColor", FogValueKind.Bool, "Mix the fog colour half and half with the weather's fog colour; the fog then turns dark in rain and at night"), new FogKey("Emission", FogValueKind.Float, "How much the fog glows by itself (0 = matte, lit by the scene only; 1 = glows in its own colour, stays bright at night)"), new FogKey("SpreadX", FogValueKind.Float, "Body cloud: how far it spreads sideways (multiplies each body part's radius)"), new FogKey("SpreadY", FogValueKind.Float, "Body cloud: how far it spreads up and down; below 1 = flatter"), new FogKey("SpreadZ", FogValueKind.Float, "Body cloud: how far it spreads to the front and back"), new FogKey("Drift", FogValueKind.Float, "Body cloud: how fast puffs rise (positive) or sink (negative), in metres per second; 0 = they stay at the body"), new FogKey("Trail", FogValueKind.Bool, "Body cloud: true = puffs stay where they appeared, so walking leaves a trail; false = they move with the body"), new FogKey("MeshRate", FogValueKind.Float, "Body cloud with FogEmitterMode Mesh: puffs per second on the whole body surface; 0 = Rate x active body parts"), new FogKey("OuterEnabled", FogValueKind.Bool, "Wide fog on or off: the large, light fog around the player"), new FogKey("OuterAnchors", FogValueKind.Text, "Wide fog: body parts it is centred on, as a comma list (Head, Chest, Hips, LeftShoulder, RightShoulder, LeftHand, RightHand, LeftUpperLeg, RightUpperLeg, LeftLowerLeg, RightLowerLeg, LeftFoot, RightFoot); one fog source each"), new FogKey("OuterRadius", FogValueKind.Float, "How far the wide fog reaches around you along the ground, in metres; most puffs sit close to you and the fog gets thinner and fainter towards this edge, where it fades out softly instead of ending in a line. Tier I default 7, tier II 9"), new FogKey("OuterAlpha", FogValueKind.Float, "How visible the wide fog around you is (0 = invisible, 1 = solid); puffs far from you are fainter (15 % at the edge). Close to you many puffs overlap, so there each puff is made fainter on its own; the fog right around you never gets thicker than about half see-through. Tier I default 0.3, tier II 0.4"), new FogKey("OuterRate", FogValueKind.Float, "Wide fog: new fog puffs per second while standing; higher = denser"), new FogKey("OuterRateDistance", FogValueKind.Float, "Wide fog: extra puffs per metre walked, so the fog keeps up with a moving player (only with OuterTrail true)"), new FogKey("OuterBurst", FogValueKind.Float, "Wide fog: puffs created at once when the effect starts, so the fog is there immediately instead of building up (Volume shape only; 0 = builds up over seconds). Tier I default 60, tier II 80"), new FogKey("OuterFollowShare", FogValueKind.Float, "Share of the wide fog that moves with you; the rest stays behind as a trail (0..1; 0.4 = 40 % of the puffs per second moves with you, within 0.7 x OuterRadius, with slightly smaller puffs that drift slowly around you; 60 % of OuterBurst starts there). Only with OuterTrail true and the Volume shape"), new FogKey("OuterSize", FogValueKind.Float, "Wide fog: size of one puff in metres. Puffs close to you are a bit smaller, puffs far out are larger and softer, and every puff grows slowly while it lives. Tier I default 4.5, tier II 5.5"), new FogKey("OuterSpreadY", FogValueKind.Float, "Wide fog with the Ring shape: band height as a fraction of OuterRadius (the Volume shape uses OuterHeightSigma instead)"), new FogKey("OuterHeightSigma", FogValueKind.Float, "Wide fog (Volume shape): how high it rises above the ground, in metres. Most puffs stay below this height, only a few rise to twice of it; puffs near the ground are smaller. Small = a low ground fog, large = a tall cloud (0.1..5). Tier I default 0.8, tier II 1"), new FogKey("OuterLifetime", FogValueKind.Float, "Wide fog: seconds a puff lingers before it fades; higher = the fog stays longer where you were. Each puff fades in slowly, drifts and grows a little, then fades out slowly. Tier I default 12, tier II 14"), new FogKey("OuterTrail", FogValueKind.Bool, "Wide fog: true = puffs stay where they appeared (you leave fog behind); false = the fog moves with you"), new FogKey("OuterRotation", FogValueKind.Float, "Wide fog: how fast it turns around you in degrees per second (negative = the other way, 0 = still)"), new FogKey("OuterOffsetY", FogValueKind.Float, "Wide fog: moves its centre up (positive) or down (negative), in metres"), new FogKey("OuterShape", FogValueKind.Text, "Wide fog shape: Volume = soft fog filling the space around you; Ring = a band of fog near the edge of OuterRadius"), new FogKey("OuterHorizontal", FogValueKind.Bool, "Wide fog: true = puffs lie flat like a carpet (seen from above); false = they face the camera like normal fog"), new FogKey("GroundEnabled", FogValueKind.Bool, "Ground fog on or off: flat fog patches at your feet that stay behind and spread, so walking leaves a field of fog"), new FogKey("GroundRate", FogValueKind.Float, "Ground fog: new patches per second, also while standing still"), new FogKey("GroundRateDistance", FogValueKind.Float, "Ground fog: extra patches per metre walked"), new FogKey("GroundSize", FogValueKind.Float, "Ground fog: size of a new patch in metres"), new FogKey("GroundGrow", FogValueKind.Float, "Ground fog: how much a patch grows before it fades (2 = to double size, 1 = no growth)"), new FogKey("GroundLifetime", FogValueKind.Float, "Ground fog: seconds a patch lingers"), new FogKey("GroundAlpha", FogValueKind.Float, "How visible the ground fog is (0 = invisible, 1 = solid)"), new FogKey("GroundRadius", FogValueKind.Float, "Ground fog: how far around your feet new patches appear, in metres"), new FogKey("GroundHeight", FogValueKind.Float, "Ground fog: height above your feet in metres (-0.5..2)"), new FogKey("GroundDrift", FogValueKind.Float, "Ground fog: how fast patches wander sideways, in metres per second"), new FogKey("DistortionStrength", FogValueKind.Float, "Distortion body look (BodyVeilMode Distortion): how strongly the body bends the view behind it"), new FogKey("DistortionColor", FogValueKind.Text, "Distortion body look: tint as r,g,b,a (a = how visible the tint is)"), new FogKey("DistortionWave", FogValueKind.Float, "Distortion body look: how fast the ripples move; negative = the game's own speed"), new FogKey("FogMaterial", FogValueKind.Text, "Texture of all fog puffs: soft (neutral soft puff, default), swamp_mist, ghost_smoke, wraith_smoke or slowwispysmoke (borrowed from the game)"), new FogKey("FogEmitterMode", FogValueKind.Text, "Where the body cloud comes from: Bones = one source per body part below; Mesh = the whole body surface (falls back to Bones if that fails)"), new FogKey("MeshOffset", FogValueKind.Float, "Body cloud with FogEmitterMode Mesh: how far off the body surface the puffs appear, in metres") }; string[] anchorNames = AnchorNames; foreach (string text in anchorNames) { list.Add(new FogKey("Anchor." + text, FogValueKind.Text, "Body cloud source on the " + text + ": on|off,radius,x,y,z (radius and offset in metres; x right, y up, z forward)")); } return list; } public string Get(string key) { if (key.StartsWith("Anchor.", StringComparison.Ordinal)) { return Anchor(key.Substring("Anchor.".Length))?.Format(); } return key switch { "Enabled" => Bool(Enabled), "Rate" => FloatList.Format(Rate), "Size" => FloatList.Format(Size), "Lifetime" => FloatList.Format(Lifetime), "Speed" => FloatList.Format(Speed), "Alpha" => FloatList.Format(Alpha), "Color" => FloatList.Format(R, G, B), "DynamicColor" => Bool(DynamicColor), "SpreadX" => FloatList.Format(SpreadX), "SpreadY" => FloatList.Format(SpreadY), "SpreadZ" => FloatList.Format(SpreadZ), "Drift" => FloatList.Format(Drift), "Trail" => Bool(Trail), "MeshRate" => FloatList.Format(MeshRate), "OuterEnabled" => Bool(OuterEnabled), "OuterAnchors" => string.Join(",", OuterAnchors), "OuterSpreadY" => FloatList.Format(OuterSpreadY), "OuterHeightSigma" => FloatList.Format(OuterHeightSigma), "OuterLifetime" => FloatList.Format(OuterLifetime), "OuterTrail" => Bool(OuterTrail), "OuterHorizontal" => Bool(OuterHorizontal), "OuterShape" => OuterShape.ToString(), "OuterRadius" => FloatList.Format(OuterRadius), "OuterAlpha" => FloatList.Format(OuterAlpha), "OuterRate" => FloatList.Format(OuterRate), "OuterRateDistance" => FloatList.Format(OuterRateDistance), "OuterSize" => FloatList.Format(OuterSize), "OuterBurst" => FloatList.Format(OuterBurst), "OuterFollowShare" => FloatList.Format(OuterFollowShare), "OuterRotation" => FloatList.Format(OuterRotation), "OuterOffsetY" => FloatList.Format(OuterOffsetY), "GroundEnabled" => Bool(GroundEnabled), "GroundRate" => FloatList.Format(GroundRate), "GroundRateDistance" => FloatList.Format(GroundRateDistance), "GroundSize" => FloatList.Format(GroundSize), "GroundGrow" => FloatList.Format(GroundGrow), "GroundLifetime" => FloatList.Format(GroundLifetime), "GroundAlpha" => FloatList.Format(GroundAlpha), "GroundRadius" => FloatList.Format(GroundRadius), "GroundHeight" => FloatList.Format(GroundHeight), "GroundDrift" => FloatList.Format(GroundDrift), "Emission" => FloatList.Format(Emission), "DistortionStrength" => FloatList.Format(DistortionStrength), "DistortionColor" => FloatList.Format(DR, DG, DB, DA), "DistortionWave" => FloatList.Format(DistortionWave), "FogMaterial" => FogMaterial, "FogEmitterMode" => EmitterMode.ToString(), "MeshOffset" => FloatList.Format(MeshOffset), _ => null, }; } private static string Bool(bool b) { if (!b) { return "false"; } return "true"; } public bool TrySet(string key, string text) { if (key.StartsWith("Anchor.", StringComparison.Ordinal)) { string name = key.Substring("Anchor.".Length); int num = Anchors.FindIndex((FogAnchor a) => string.Equals(a.Name, name, StringComparison.OrdinalIgnoreCase)); if (num < 0 || !FogAnchor.TryParse(Anchors[num].Name, text, out var anchor)) { return false; } Anchors[num] = anchor; return true; } switch (key) { case "Enabled": return SetBool(text, ref Enabled); case "DynamicColor": return SetBool(text, ref DynamicColor); case "OuterEnabled": return SetBool(text, ref OuterEnabled); case "Trail": return SetBool(text, ref Trail); case "OuterTrail": return SetBool(text, ref OuterTrail); case "OuterHorizontal": return SetBool(text, ref OuterHorizontal); case "GroundEnabled": return SetBool(text, ref GroundEnabled); case "OuterAnchors": { if (!TryParseAnchorList(text, out var names)) { return false; } OuterAnchors = names; return true; } case "Color": { if (!FloatList.TryParse(text, 3, out var values)) { return false; } R = Clamp01(values[0]); G = Clamp01(values[1]); B = Clamp01(values[2]); return true; } case "DistortionColor": { if (!FloatList.TryParse(text, 4, out var values2)) { return false; } DR = Clamp01(values2[0]); DG = Clamp01(values2[1]); DB = Clamp01(values2[2]); DA = Clamp01(values2[3]); return true; } case "FogMaterial": { string text2 = NormalizeFogMaterial(text); if (text2 == null) { return false; } FogMaterial = text2; return true; } case "OuterShape": { if (!TryParseOuterShape(text, out var shape)) { return false; } OuterShape = shape; return true; } case "FogEmitterMode": { if (!TryParseEmitterMode(text, out var mode)) { return false; } EmitterMode = mode; return true; } default: { if (!FloatList.TryParseOne(text, out var value)) { return false; } switch (key) { case "Rate": Rate = Math.Max(0f, value); return true; case "Size": Size = Math.Max(0.01f, value); return true; case "Lifetime": Lifetime = Math.Max(0.05f, value); return true; case "Speed": Speed = value; return true; case "Alpha": Alpha = Clamp01(value); return true; case "SpreadX": SpreadX = Math.Max(0.01f, value); return true; case "SpreadY": SpreadY = Math.Max(0.01f, value); return true; case "SpreadZ": SpreadZ = Math.Max(0.01f, value); return true; case "Drift": Drift = value; return true; case "OuterRadius": OuterRadius = Math.Max(0f, value); return true; case "OuterAlpha": OuterAlpha = Clamp01(value); return true; case "OuterRate": OuterRate = Math.Max(0f, value); return true; case "OuterRateDistance": OuterRateDistance = Math.Max(0f, value); return true; case "OuterSize": OuterSize = Math.Max(0.01f, value); return true; case "OuterBurst": OuterBurst = Math.Max(0f, value); return true; case "OuterFollowShare": OuterFollowShare = Clamp01(value); return true; case "Emission": Emission = Clamp01(value); return true; case "DistortionStrength": DistortionStrength = Math.Max(0f, value); return true; case "DistortionWave": DistortionWave = ((value < 0f) ? (-1f) : value); return true; case "MeshOffset": MeshOffset = Math.Max(0f, value); return true; case "MeshRate": MeshRate = Math.Max(0f, value); return true; case "OuterSpreadY": OuterSpreadY = Math.Max(0.01f, value); return true; case "OuterHeightSigma": OuterHeightSigma = ((value < 0.1f) ? 0.1f : ((value > 5f) ? 5f : value)); return true; case "OuterLifetime": OuterLifetime = Math.Max(0.05f, value); return true; case "OuterRotation": OuterRotation = ((value < -180f) ? (-180f) : ((value > 180f) ? 180f : value)); return true; case "OuterOffsetY": OuterOffsetY = ((value < -2f) ? (-2f) : ((value > 2f) ? 2f : value)); return true; case "GroundRate": GroundRate = Math.Max(0f, value); return true; case "GroundRateDistance": GroundRateDistance = Math.Max(0f, value); return true; case "GroundSize": GroundSize = Math.Max(0.01f, value); return true; case "GroundGrow": GroundGrow = Math.Max(0.1f, value); return true; case "GroundLifetime": GroundLifetime = Math.Max(0.05f, value); return true; case "GroundAlpha": GroundAlpha = Clamp01(value); return true; case "GroundRadius": GroundRadius = Math.Max(0f, value); return true; case "GroundHeight": GroundHeight = ((value < -0.5f) ? (-0.5f) : ((value > 2f) ? 2f : value)); return true; case "GroundDrift": GroundDrift = Math.Max(0f, value); return true; default: return false; } } } } private static bool SetBool(string text, ref bool field) { if (!FogAnchor.TryParseSwitch(text, out var on)) { return false; } field = on; return true; } private static float Clamp01(float v) { if (!(v < 0f)) { if (!(v > 1f)) { return v; } return 1f; } return 0f; } public static FogSettings Parse(int tier, Func<string, string> read, List<string> warnings) { FogSettings fogSettings = Defaults(tier); foreach (FogKey key in Keys) { string text = read(key.Name); if (text != null && !fogSettings.TrySet(key.Name, text)) { warnings?.Add(key.Name + " '" + text + "' is malformed; using default " + fogSettings.Get(key.Name)); } } return fogSettings; } } public struct FogVolumePoint { public float X; public float Height; public float Z; public float SizeFactor; public float AlphaFactor; public FogVolumePoint(float x, float height, float z, float sizeFactor, float alphaFactor) { X = x; Height = height; Z = z; SizeFactor = sizeFactor; AlphaFactor = alphaFactor; } } public static class FogVolumeShape { public const float DefaultHeightSigma = 0.9f; public const float HeightFloor = 0.1f; public const float MaxHeightSigma = 5f; public const float MaxHeightSigmas = 3f; public const float SizeAtGround = 0.7f; public const float FullSizeSigmas = 2f; public const float RadialSigmaFactor = 0.45f; public const float AlphaAtRim = 0.15f; public const float RadialSizeAtCentre = 0.8f; public const float RadialSizeAtRim = 1.2f; private static readonly double TruncatedShare = 1.0 - Gauss(1.0); private static double Gauss(double x) { return Math.Exp((0.0 - x) * x / 0.40499997854232817); } public static float AreaDensity(float r, float radius) { if (!(radius > 0f)) { return 0f; } float num = Math.Abs(r) / radius; if (!(num > 1f)) { return (float)Gauss(num); } return 0f; } public static double RadiusShare(double x) { if (!(x <= 0.0)) { if (!(x >= 1.0)) { return (1.0 - Gauss(x)) / TruncatedShare; } return 1.0; } return 0.0; } public static float RadiusAt(float u, float radius) { if (!(radius > 0f)) { return 0f; } double num = Clamp01(u); double num2 = 1.0 - num * TruncatedShare; double val = ((num2 <= 0.0) ? 1.0 : (0.44999998807907104 * Math.Sqrt(Math.Max(0.0, -2.0 * Math.Log(num2))))); return (float)Math.Min(1.0, val) * radius; } public static float HeightDensity(float h, float sigma) { float num = Math.Max(0.1f, sigma); if (!(h < 0f)) { return (float)Math.Exp((0.0 - (double)h) * (double)h / (2.0 * (double)num * (double)num)); } return 0f; } public static float HeightAt(float u, float sigma) { float num = Math.Max(0.1f, sigma); float num2 = Clamp01(u); double num3 = 0.0; double num4 = 3.0; if (HalfNormalShare(num4) <= (double)num2) { return Math.Max(0.1f, 3f * num); } for (int i = 0; i < 40; i++) { double num5 = 0.5 * (num3 + num4); if (HalfNormalShare(num5) < (double)num2) { num3 = num5; } else { num4 = num5; } } return Math.Max(0.1f, (float)(0.5 * (num3 + num4)) * num); } public static float SizeFactor(float h, float sigma) { float num = 2f * Math.Max(0.1f, sigma); return 0.7f + 0.3f * Clamp01(h / num); } public static float RadialSizeFactor(float r, float radius) { if (!(radius > 0f)) { return 1f; } return 0.8f + 0.40000004f * Clamp01(r / radius); } public static float AlphaFactor(float r, float radius) { if (!(radius > 0f)) { return 1f; } float num = Clamp01(Math.Abs(r) / radius); return 0.15f + 0.85f * (float)((Gauss(num) - Gauss(1.0)) / TruncatedShare); } public static float CappedAlphaFactor(float r, float radius, FogOverlapCap cap) { return Math.Min(AlphaFactor(r, radius), cap.FactorLimit(r, radius)); } public static FogVolumePoint Sample(float uRadius, float uAngle, float uHeight, float radius, float sigma, FogOverlapCap cap = default(FogOverlapCap)) { return At(RadiusAt(uRadius, radius), (double)uAngle * 2.0 * Math.PI, HeightAt(uHeight, sigma), radius, sigma, cap); } internal static FogVolumePoint At(float r, double angle, float h, float radius, float sigma, FogOverlapCap cap) { return new FogVolumePoint((float)((double)r * Math.Cos(angle)), h, (float)((double)r * Math.Sin(angle)), SizeFactor(h, sigma) * RadialSizeFactor(r, radius), CappedAlphaFactor(r, radius, cap)); } private static double HalfNormalShare(double z) { return Erf(z / Math.Sqrt(2.0)); } private static double Erf(double x) { if (x <= 0.0) { return 0.0; } double num = 1.0 / (1.0 + 0.3275911 * x); double num2 = num * (0.254829592 + num * (-0.284496736 + num * (1.421413741 + num * (-1.453152027 + num * 1.061405429)))); return 1.0 - num2 * Math.Exp((0.0 - x) * x); } private static float Clamp01(float v) { if (!(v < 0f)) { if (!(v > 1f)) { return v; } return 1f; } return 0f; } } public readonly struct FogOverlapCap { public readonly float CentreOverlap; public readonly float PuffOpacity; public readonly float MaxOpacity; public bool Active { get { if (CentreOverlap > 0f && PuffOpacity > 0f && MaxOpacity > 0f) { return MaxOpacity < 1f; } return false; } } public FogOverlapCap(float centreOverlap, float puffOpacity, float maxOpacity) { CentreOverlap = centreOverlap; PuffOpacity = puffOpacity; MaxOpacity = maxOpacity; } public float Overlap(float r, float radius) { if (!Active) { return 0f; } return CentreOverlap * FogVolumeShape.AreaDensity(r, radius); } public float FactorLimit(float r, float radius) { if (!Active) { return 1f; } float num = Math.Max(1f, Overlap(r, radius)); double num2 = 1.0 - Math.Pow(1.0 - (double)MaxOpacity, 1.0 / (double)num); return (float)Math.Min(1.0, num2 / (double)PuffOpacity); } public float ColumnOpacity(float r, float radius, float factor) { float num = Math.Min(1f, Math.Max(0f, PuffOpacity * factor)); return (float)(1.0 - Math.Pow(1.0 - (double)num, Overlap(r, radius))); } } public static class FogBurstPattern { public const double GoldenAngle = 2.3999632297286535; public static FogVolumePoint Point(int index, int count, float radius, float sigma, FogOverlapCap cap = default(FogOverlapCap)) { if (count <= 0 || index < 0) { return default(FogVolumePoint); } float u = (float)(((double)index + 0.5) / (double)count); return FogVolumeShape.At(FogVolumeShape.RadiusAt(u, radius), (double)index * 2.3999632297286535, FogVolumeShape.HeightAt((float)RadicalInverse2(index + 1), sigma), radius, sigma, cap); } private static double RadicalInverse2(int n) { double num = 0.0; double num2 = 0.5; while (n > 0) { if ((n & 1) != 0) { num += num2; } n >>= 1; num2 *= 0.5; } return num; } } public static class FogMaterialCheck { public static readonly string[] BlendProperties = new string[6] { "_BlendOp", "_SrcBlend", "_DstBlend", "_AlphaChannel", "_ZWrite", "_Mode" }; private const float Epsilon = 0.002f; public static List<string> Problems(FogRgb configured, FogRgb color, FogRgb emission, float emissionSetting) { List<string> list = new List<string>(); if (color.R > configured.R + 0.002f || color.G > configured.G + 0.002f || color.B > configured.B + 0.002f) { list.Add("_Color rgb " + FloatList.Format(color.R, color.G, color.B) + " is above the configured " + FloatList.Format(configured.R, configured.G, configured.B)); } if (emissionSetting <= 0f && (emission.R > 0.002f || emission.G > 0.002f || emission.B > 0.002f)) { list.Add("_EmissionColor rgb " + FloatList.Format(emission.R, emission.G, emission.B) + " is not black at Emission 0"); } return list; } } public static class FogSprite { public const int Size = 128; public const float CentreAlpha = 0.8f; public const float Sharpness = 4f; public const float NoiseAmount = 0.1f; public const int NoiseCells = 6; private const int Seed = 7919; public static readonly float MeanDiscAlpha = (float)(0.800000011920929 * ((1.0 - Math.Exp(-4.0)) / 4.0 - Math.Exp(-4.0)) / (1.0 - Math.Exp(-4.0))); public static float Falloff(float r) { if (r >= 1f) { return 0f; } if (r < 0f) { r = 0f; } double num = Math.Exp(-4.0); return 0.8f * (float)((Math.Exp(-4f * r * r) - num) / (1.0 - num)); } public static float Radius(int x, int y, int size) { float num = (float)(size - 1) * 0.5f; float num2 = ((float)x - num) / num; float num3 = ((float)y - num) / num; return (float)Math.Sqrt(num2 * num2 + num3 * num3); } public static float Noise(int x, int y, int size) { float num = (float)x / (float)Math.Max(1, size - 1) * 6f; float num2 = (float)y / (float)Math.Max(1, size - 1) * 6f; int num3 = (int)Math.Floor(num); int num4 = (int)Math.Floor(num2); float num5 = Smooth(num - (float)num3); float num6 = Smooth(num2 - (float)num4); float num7 = Lattice(num3, num4) + (Lattice(num3 + 1, num4) - Lattice(num3, num4)) * num5; float num8 = Lattice(num3, num4 + 1) + (Lattice(num3 + 1, num4 + 1) - Lattice(num3, num4 + 1)) * num5; return num7 + (num8 - num7) * num6; } public static float Alpha(int x, int y, int size) { float num = Falloff(Radius(x, y, size)); if (num <= 0f) { return 0f; } float num2 = num * (1f + 0.1f * Noise(x, y, size)); if (!(num2 < 0f)) { if (!(num2 > 0.8f)) { return num2; } return 0.8f; } return 0f; } private static float Smooth(float t) { return t * t * (3f - 2f * t); } private static float Lattice(int x, int y) { uint num = (uint)((x * 73856093) ^ (y * 19349663) ^ 0x1EEF); num ^= num >> 13; num *= 1540483477; num ^= num >> 15; return (float)(num & 0xFFFF) / 32767.5f - 1f; } } public static class FloatList { public static bool TryParseOne(string text, out float value) { if (!float.TryParse((text ?? "").Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out value)) { return false; } if (float.IsNaN(value) || float.IsInfinity(value)) { value = 0f; return false; } return true; } public static bool TryParse(string text, int count, out float[] values) { values = null; if (string.IsNullOrWhiteSpace(text)) { return false; } string[] array = text.Split(new char[1] { ',' }); if (array.Length != count) { return false; } float[] array2 = new float[count]; for (int i = 0; i < count; i++) { if (!TryParseOne(array[i], out array2[i])) { return false; } } values = array2; return true; } public static string Format(float value) { return value.ToString("0.####", CultureInfo.InvariantCulture); } public static string Format(params float[] values) { string[] array = new string[values.Length]; for (int i = 0; i < values.Length; i++) { array[i] = Format(values[i]); } return string.Join(",", array); } } public enum BodyVeilMode { Off, Cutoff, Hide, Tint, Ghost, Distortion, Shadow, Spirit } public sealed class FogVeil : IVeil { private sealed class Snapshot { public int Tier; public bool IsLocal; public bool ForceHide; public int Version; public BodyVeilMode Requested; public BodyVeilMode Effective; public bool FogWanted; public float BuiltAt; public readonly List<FogEmitter> Fog = new List<FogEmitter>(); public readonly List<Material> FogMaterials = new List<Material>(); public bool MaterialHasColor; public Color AppliedColor; public readonly Dictionary<Material, float> Cutoffs = new Dictionary<Material, float>(); public readonly Dictionary<Material, Color> Colors = new Dictionary<Material, Color>(); public readonly Dictionary<Renderer, Material[]> SharedMaterials = new Dictionary<Renderer, Material[]>(); public readonly HashSet<Renderer> Hidden = new HashSet<Renderer>(); public readonly Dictionary<Material, Material> SpiritByOriginal = new Dictionary<Material, Material>(); public readonly List<Material> SpiritOwned = new List<Material>(); public bool FogIntact() { if (!FogWanted) { return true; } if (Fog.Count == 0) { return false; } foreach (FogEmitter item in Fog) { if ((Object)(object)item.Go == (Object)null) { return false; } } return true; } } private sealed class FogEmitter { public GameObject Go; public ParticleSystem Ps; public string Anchor; public FogLayerKind Layer; public float VertexAlpha; public int Burst; } private sealed class FogLayer { public float Rate; public float Size; public float Lifetime; public float SpreadX; public float SpreadY; public float SpreadZ; public float VertexAlpha; public FogTrailMode Trail; public bool Horizontal; public bool Ring; public bool Volume; public float SizeMinFactor = 0.6f; public float SizeMaxFactor = 1.25f; public float Speed = float.NaN; public float Orbital; public float RateDistance; public float Grow = 1f; public int MaxParticles; public int Burst; public bool PositionOnly; public bool EvenBurst; public float HeightSigma = 0.9f; public float GroundOffsetY; public FogOverlapCap Cap; public Material Material; } private static readonly int CutoffId = Shader.PropertyToID("_Cutoff"); private static readonly int ColorId = Shader.PropertyToID("_Color"); private static readonly int EmissionColorId = Shader.PropertyToID("_EmissionColor"); private static readonly int RefractionId = Shader.PropertyToID("_RefractionIntensity"); private static readonly int GlossinessId = Shader.PropertyToID("_Glossiness"); private static readonly int MetallicId = Shader.PropertyToID("_Metallic"); private static readonly int NormalTexId = Shader.PropertyToID("_NormalTex"); private static readonly int MainTexId = Shader.PropertyToID("_MainTex"); private static readonly int NormalScaleId = Shader.PropertyToID("_NormalScale"); private static readonly int WaveVelId = Shader.PropertyToID("_WaveVel"); private static readonly int TintColorId = Shader.PropertyToID("_TintColor"); private static readonly int ZFadeDistanceId = Shader.PropertyToID("_ZFadeDistance"); private static readonly int CameraFadeMinId = Shader.PropertyToID("_CameraFadeDistanceMin"); private static readonly int CameraFadeMaxId = Shader.PropertyToID("_CameraFadeDistanceMax"); private static readonly int CameraYFadeId = Shader.PropertyToID("_CameraYFadeDistance"); private static readonly int BillboardId = Shader.PropertyToID("_Billboard"); private static readonly int LightNormalFactorId = Shader.PropertyToID("_LightNormalFactor"); private static readonly int SkyMaskId = Shader.PropertyToID("_SkyMask"); private const float FogZFadeDistance = 0.25f; private const float FogCameraFadeMin = 0.3f; private const float FogCameraFadeMax = 1f; private static readonly Color TintTarget = new Color(0.3f, 0.35f, 0.4f); private const float TintAmount = 0.7f; private const string FogMaterialSource = "vfx_swamp_mist"; private const string GhostPrefabName = "Ghost"; private const string DistortionShaderName = "Custom/Distortion"; private const string PreferredDistortionMaterial = "Aspect_mat"; public const string FogObjectName = "ip_fog"; private const string OuterObjectName = "ip_fog_outer"; private const string OuterFollowObjectName = "ip_fog_outer_follow"; private const string GroundObjectName = "ip_fog_ground"; private const string GroundAnchorName = "Ground"; private const string GroundUpperAnchorName = "GroundUpper"; private const float DynamicColorBlend = 0.5f; public static readonly FogSettings[] Fog = new FogSettings[4] { null, FogSettings.Defaults(1), FogSettings.Defaults(2), FogSettings.Defaults(3) }; public static FogAlphaMode AlphaMode = FogAlphaMode.Both; public static Color GhostColor = new Color(0.75f, 0.8f, 0.9f, 1f); public static float GhostEmission = 0.25f; public static Color ShadowColor = DefaultShadowColor; public static Color SpiritColor = DefaultSpiritColor; public static float SpiritStrength = 0.25f; public static readonly Color DefaultShadowColor = new Color(0f, 0f, 0f, 0.12f); public static readonly Color DefaultSpiritColor = new Color(0.6f, 0.7f, 0.8f, 1f); public const float DefaultSpiritStrength = 0.25f; public static readonly BodyVeilMode?[] ModeOverride = new BodyVeilMode?[4]; private static readonly BodyVeilMode[] _configuredModes = new BodyVeilMode[4] { BodyVeilMode.Off, BodyVeilMode.Off, BodyVeilMode.Distortion, BodyVeilMode.Distortion }; private readonly Dictionary<Player, Snapshot> _snapshots = new Dictionary<Player, Snapshot>(); private static readonly List<Material> MatBuf = new List<Material>(); private static readonly List<Renderer> RendererBuf = new List<Renderer>(); private static bool _fogWarned; private static bool _noAnimatorWarned; private static readonly HashSet<string> _missingBoneWarned = new HashSet<string>(); private static readonly Dictionary<string, string[]> BoneFallbacks = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase) { ["Chest"] = new string[4] { "Spine2", "Spine1", "Spine", "UpperChest" }, ["Hips"] = new string[1] { "Pelvis" }, ["LeftShoulder"] = new string[2] { "LeftArm", "LeftShoulder" }, ["RightShoulder"] = new string[2] { "RightArm", "RightShoulder" }, ["LeftUpperLeg"] = new string[1] { "LeftUpLeg" }, ["RightUpperLeg"] = new string[1] { "RightUpLeg" }, ["LeftLowerLeg"] = new string[1] { "LeftLeg" }, ["RightLowerLeg"] = new string[1] { "RightLeg" } }; private static readonly Dictionary<string, HumanBodyBones> AnchorBones = new Dictionary<string, HumanBodyBones>(StringComparer.OrdinalIgnoreCase) { ["LeftShoulder"] = (HumanBodyBones)13, ["RightShoulder"] = (HumanBodyBones)14 }; private static readonly Particle[] BurstBuf = (Particle[])(object)new Particle[300]; private const float GroundStartSpeed = 0.01f; private const float GroundShapeFlatten = 0.05f; private static readonly HashSet<string> _meshWarned = new HashSet<string>(); private const string MeshAnchorName = "Mesh"; private static readonly Dictionary<string, KeyValuePair<string, string[]>> FogMaterialSources = new Dictionary<string, KeyValuePair<string, string[]>> { ["ghost_smoke"] = new KeyValuePair<string, string[]>("ghost_smoke", new string[1] { "Ghost_Void" }), ["wraith_smoke"] = new KeyValuePair<string, string[]>("wraith_smoke", new string[2] { "Wraith", "vfx_ghost_spawn" }), ["slowwispysmoke"] = new KeyValuePair<string, string[]>("slowwispysmoke_gradient_alphablend", new string[1] { "Ghost" }) }; private static readonly Dictionary<string, Material> _fogMaterials = new Dictionary<string, Material>(); private static readonly HashSet<string> _fogMaterialMissing = new HashSet<string>(); private static Material _fogMaterial; private static Material _softFogMaterial; private static Material _ghostSource; private static Material _ghostInstance; private static Color _ghostSourceEmission = Color.black; private const string ShadowPrefabName = "ShadowPerson"; private const string ShadowMaterialName = "ShadowPerson 1"; private static Material _shadowSource; private static Material _shadowInstance; private static bool _shadowSearched; private const string SpiritShaderName = "Custom/Fallen Warrior"; private static readonly string[] SpiritPrefabNames = new string[5] { "FallenWarrior", "Wolf_spiritcaller", "Boar_spiritcaller", "Moose_spiritcaller", "Bjorn_spiritcaller" }; private static readonly string[] SpiritTextureSlots = new string[6] { "_MainTex", "_SkinBumpMap", "_ChestTex", "_ChestBumpMap", "_LegsTex", "_LegsBumpMap" }; private static readonly int SkinColorId = Shader.PropertyToID("_SkinColor"); private static readonly int BumpScaleId = Shader.PropertyToID("_BumpScale"); private static Shader _spiritShader; private static Material _spiritSource; private static bool _spiritSearched; private const string ShieldPrefabName = "vfx_StaffShield"; private const string ShieldMaterialName = "staff_shield_shard"; private static bool _shieldSearched; private static Texture _shieldNormal; private static float _shieldNormalScale = float.NaN; private static readonly Material[] _distortionInstances = (Material[])(object)new Material[4]; private static Material _distortionSource; private static bool _distortionSearched; private static bool _distortionLogged; public static int Version { get; private set; } public static float BorrowedDistortionWave { get; private set; } = float.NaN; public static void Bump() { Version++; if ((Object)(object)_ghostInstance != (Object)null) { ApplyGhostLook(_ghostInstance); } if ((Object)(object)_shadowInstance != (Object)null) { ApplyShadowLook(_shadowInstance); } for (int i = 1; i <= 3; i++) { if ((Object)(object)_distortionInstances[i] != (Object)null) { ApplyDistortionLook(_distortionInstances[i], i); } } } public static void LoadFromConfig() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //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_0079: 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_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) GlobalConfig global = PluginConfig.Global; for (int i = 1; i <= 3; i++) { Fog[i] = PluginConfig.Fog(i).Clone(); } AlphaMode = PluginConfig.FogAlphaMode; GhostColor = ParseColor(global.GhostColor, 4, new Color(0.75f, 0.8f, 0.9f, 1f), "GhostColor"); GhostEmission = Mathf.Max(0f, global.GhostEmission); ShadowColor = ParseColor(global.ShadowColor, 4, DefaultShadowColor, "ShadowColor"); SpiritColor = ParseColor(global.SpiritColor, 3, DefaultSpiritColor, "SpiritColor"); SpiritStrength = Mathf.Max(0f, global.SpiritStrength); ParseConfiguredModes(); Bump(); } private static Color ParseColor(string text, int count, Color fallback, string key) { //IL_0078: 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) if (FloatList.TryParse(text, count, out var values)) { return new Color(values[0], values[1], values[2], (count > 3) ? values[3] : 1f); } Plugin.Log.LogWarning((object)("[Veil] " + key + " '" + text + "' is not " + ((count > 3) ? "r,g,b,a" : "r,g,b") + "; using default")); return fallback; } public static string FormatColor(Color c) { //IL_0008: 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_001a: 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) return FloatList.Format(c.r, c.g, c.b, c.a); } public static string FormatRgb(Color c) { //IL_0008: 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_001a: Unknown result type (might be due to invalid IL or missing references) return FloatList.Format(c.r, c.g, c.b); } public static void SaveGlobalLook() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) PluginConfig.SaveVeilLook(FormatColor(GhostColor), GhostEmission, FormatColor(ShadowColor), FormatRgb(SpiritColor), SpiritStrength); } public static bool TryParseMode(string text, out BodyVeilMode mode) { if (!string.IsNullOrEmpty(text) && Enum.TryParse<BodyVeilMode>(text.Trim(), ignoreCase: true, out mode) && Enum.IsDefined(typeof(BodyVeilMode), mode)) { return true; } mode = BodyVeilMode.Off; return false; } public static BodyVeilMode ConfiguredMode(int tier) { if (tier < 1 || tier > 3) { return BodyVeilMode.Off; } return _configuredModes[tier]; } private static void ParseConfiguredModes() { for (int i = 1; i <= 3; i++) { string bodyVeilMode = PluginConfig.Tier(i).BodyVeilMode; if (TryParseMode(bodyVeilMode, out var mode)) { _configuredModes[i] = mode; continue; } string text = PluginConfig.DefaultBodyVeilMode(i); TryParseMode(text, out mode); Plugin.Log.LogWarning((object)string.Format("[Tier{0}] BodyVeilMode '{1}' is not one of {2}; using the default {3}", i, bodyVeilMode, string.Join(", ", Enum.GetNames(typeof(BodyVeilMode))), mode)); _configuredModes[i] = mode; } } public static BodyVeilMode ModeFor(int tier) { if (tier < 1 || tier > 3) { return BodyVeilMode.Off; } return ModeOverride[tier] ?? ConfiguredMode(tier); } public void Apply(Player p, int tier, bool isLocal, bool forceHide) { if ((Object)(object)p == (Object)null) { return; } BodyVeilMode bodyVeilMode = (forceHide ? BodyVeilMode.Hide : ModeFor(tier)); if (_snapshots.TryGetValue(p, out var value) && value.Tier == tier && value.IsLocal == isLocal && value.ForceHide == forceHide && value.Version == Version && value.Requested == bodyVeilMode && value.FogIntact()) { ApplyBody(p, value); UpdateFogColor(value); return; } Remove(p); FogSettings fogSettings = Fog[tier]; Snapshot snapshot = new Snapshot { Tier = tier, IsLocal = isLocal, ForceHide = forceHide, Version = Version, Requested = bodyVeilMode, Effective = Resolve(bodyVeilMode, isLocal, tier), FogWanted = (!forceHide && fogSettings.Enabled && (fogSettings.InnerActive || fogSettings.OuterActive || fogSettings.GroundActive)), BuiltAt = Time.time }; _snapshots[p] = snapshot; if (snapshot.FogWanted) { SpawnFog(p, snapshot); } if (snapshot.Fog.Count == 0) { snapshot.FogWanted = false; } ApplyBody(p, snapshot); Plugin.Log.LogDebug((object)(string.Format("veil T{0} on {1}: mode {2} (effective {3}){4}, ", tier, p.GetPlayerName(), snapshot.Requested, snapshot.Effective, forceHide ? " [hidden from players]" : "") + $"fog {snapshot.Fog.Count} emitters, {snapshot.Hidden.Count} hidden, {snapshot.SharedMaterials.Count} swapped, {snapshot.Colors.Count} tinted, {snapshot.Cutoffs.Count} cut")); } private static BodyVeilMode Resolve(BodyVeilMode mode, bool isLocal, int tier) { switch (mode) { case BodyVeilMode.Hide: if (!isLocal || !PluginConfig.Global.ShowSelfFaintly) { return BodyVeilMode.Hide; } return BodyVeilMode.Tint; case BodyVeilMode.Ghost: if ((Object)(object)GhostInstance() != (Object)null) { return BodyVeilMode.Ghost; } Plugin.Log.LogWarning((object)"veil mode Ghost: no material found on prefab 'Ghost'; using Tint"); return BodyVeilMode.Tint; case BodyVeilMode.Distortion: if ((Object)(object)DistortionInstance(tier) != (Object)null) { return BodyVeilMode.Distortion; } Plugin.Log.LogWarning((object)"veil mode Distortion: shader 'Custom/Distortion' not found; using Tint"); return BodyVeilMode.Tint; case BodyVeilMode.Shadow: if ((Object)(object)ShadowInstance() != (Object)null) { return BodyVeilMode.Shadow; } Plugin.Log.LogWarning((object)"veil mode Shadow: material 'ShadowPerson 1' not found (prefab ShadowPerson, SP_* items, loaded materials); using Tint"); return BodyVeilMode.Tint; case BodyVeilMode.Spirit: if ((Object)(object)SpiritShader() != (Object)null) { return BodyVeilMode.Spirit; } Plugin.Log.LogWarning((object)"veil mode Spirit: shader 'Custom/Fallen Warrior' not found; using Tint"); return BodyVeilMode.Tint; default: return mode; } } private static void ApplyBody(Player p, Snapshot snap) { //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) if (snap.Effective == BodyVeilMode.Off) { return; } GlobalConfig global = PluginConfig.Global; float num = ((snap.IsLocal && global.ShowSelfFaintly) ? global.FogCutoffSelf : ((snap.Tier == 1) ? global.FogCutoffLight : global.FogCutoffDense)); Material val = ((snap.Effective == BodyVeilMode.Ghost) ? GhostInstance() : ((snap.Effective == BodyVeilMode.Distortion) ? DistortionInstance(snap.Tier) : ((snap.Effective == BodyVeilMode.Shadow) ? ShadowInstance() : null))); bool flag = IsSwapMode(snap.Effective); ((Component)p).GetComponentsInChildren<Renderer>(true, RendererBuf); foreach (Renderer item in RendererBuf) { if ((Object)(object)item == (Object)null || IsOurs(((Component)item).gameObject)) { continue; } if (flag && item is ParticleSystemRenderer) { if (item.enabled) { snap.Hidden.Add(item); item.enabled = false; } continue; } switch (snap.Effective) { case BodyVeilMode.Cutoff: item.GetMaterials(MatBuf); foreach (Material item2 in MatBuf) { if (!((Object)(object)item2 == (Object)null) && item2.HasProperty(CutoffId)) { if (!snap.Cutoffs.ContainsKey(item2)) { snap.Cutoffs[item2] = item2.GetFloat(CutoffId); } item2.SetFloat(CutoffId, num); } } break; case BodyVeilMode.Hide: if (item.enabled) { snap.Hidden.Add(item); item.enabled = false; } break; case BodyVeilMode.Tint: if (!IsBodyRenderer(item)) { break; } item.GetMaterials(MatBuf); foreach (Material item3 in MatBuf) { if (!((Object)(object)item3 == (Object)null) && item3.HasProperty(ColorId)) { if (!snap.Colors.TryGetValue(item3, out var value)) { value = (snap.Colors[item3] = item3.GetColor(ColorId)); } Color val2 = Color.Lerp(value, TintTarget, 0.7f); val2.a = value.a; item3.SetColor(ColorId, val2); } } break; case BodyVeilMode.Ghost: case BodyVeilMode.Distortion: case BodyVeilMode.Shadow: if (!((Object)(object)val == (Object)null) && IsBodyRenderer(item) && !snap.SharedMaterials.ContainsKey(item)) { Material[] sharedMaterials2 = item.sharedMaterials; snap.SharedMaterials[item] = sharedMaterials2; Material[] array2 = (Material[])(object)new Material[sharedMaterials2.Length]; for (int j = 0; j < array2.Length; j++) { array2[j] = val; } item.sharedMaterials = array2; } break; case BodyVeilMode.Spirit: if (IsBodyRenderer(item) && !snap.SharedMaterials.ContainsKey(item)) { Material[] sharedMaterials = item.sharedMaterials; Material[] array = (Material[])(object)new Material[sharedMaterials.Length]; for (int i = 0; i < array.Length; i++) { array[i] = SpiritFor(snap, sharedMaterials[i]); } snap.SharedMaterials[item] = sharedMaterials; item.sharedMaterials = array; } break; } } MatBuf.Clear(); } private static bool IsSwapMode(BodyVeilMode m) { if (m != BodyVeilMode.Ghost && m != BodyVeilMode.Distortion && m != BodyVeilMode.Shadow) { return m == BodyVeilMode.Spirit; } return true; } private static bool IsBodyRenderer(Renderer r) { if (!(r is SkinnedMeshRenderer)) { return r is MeshRenderer; } return true; } private static bool IsOurs(GameObject go) { return ((Object)go).name.StartsWith("ip_fog", StringComparison.Ordinal); } private static Transform FindBone(Animator animator, string anchorName) { //IL_0010: 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_0045: Unknown result type (might be due to invalid IL or missing references) HumanBodyBones result; if (AnchorBones.TryGetValue(anchorName, out var value)) { Transform boneTransform = Utils.GetBoneTransform(animator, value); if ((Object)(object)boneTransform != (Object)null) { return boneTransform; } } else if (Enum.TryParse<HumanBodyBones>(anchorName, ignoreCase: true, out result) && Enum.IsDefined(typeof(HumanBodyBones), result)) { Transform boneTransform2 = Utils.GetBoneTransform(animator, result); if ((Object)(object)boneTransform2 != (Object)null) { return boneTransform2; } } if (BoneFallbacks.TryGetValue(anchorName, out var value2)) { string[] array = value2; foreach (string text in array) { Transform val = Utils.FindChild(((Component)animator).transform, text, (IterativeSearchType)0); if ((Object)(object)val != (Object)null) { return val; } } } return null; } private static void SpawnFog(Player p, Snapshot snap) { //IL_0050: 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_0057: 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_0085: 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_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_03b4: Unknown result type (might be due to invalid IL or missing references) //IL_03e8: Unknown result type (might be due to invalid IL or missing references) //IL_0362: Unknown result type (might be due to invalid IL or missing references) //IL_0367: Unknown result type (might be due to invalid IL or missing references) //IL_02ee: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_0522: Unknown result type (might be due to invalid IL or missing references) //IL_0524: Unknown result type (might be due to invalid IL or missing references) //IL_057e: Unknown result type (might be due to invalid IL or missing references) //IL_0580: Unknown result type (might be due to invalid IL or missing references) FogSettings s = Fog[snap.Tier]; Material val = FogSourceMaterial(s.FogMaterial); if ((Object)(object)val == (Object)null) { if (!_fogWarned) { _fogWarned = true; Plugin.Log.LogWarning((object)"veil fog: no particle material available; fog skipped"); } return; } Color color = (snap.AppliedColor = FogColor(s)); bool innerActive = s.InnerActive; bool groundActive = s.GroundActive; float vertexAlpha = 0f; Material val2 = (innerActive ? MakeFogMaterial(val, snap, color, s.Alpha, s.Emission, out vertexAlpha) : null); Material outer = null; float outerVertexAlpha = 0f; if (s.OuterActive) { outer = MakeFogMaterial(val, snap, color, s.OuterAlpha, s.Emission, out outerVertexAlpha); } float vertexAlpha2 = 0f; Material val3 = (groundActive ? MakeFogMaterial(val, snap, color, s.GroundAlpha, s.Emission, out vertexAlpha2) : null); float vertexAlpha3 = 0f; Material val4 = (groundActive ? MakeFogMaterial(val, snap, color, s.GroundUpperAlpha, s.Emission, out vertexAlpha3) : null); Animator animator = ((Character)p).m_animator; if ((Object)(object)animator == (Object)null && !_noAnimatorWarned) { _noAnimatorWarned = true; Plugin.Log.LogWarning((object)"veil fog: player has no animator; using one emitter at the player root"); } FogLayer fogLayer = new FogLayer { Rate = s.Rate, Size = s.Size, Lifetime = s.Lifetime, SpreadX = s.SpreadX, SpreadY = s.SpreadY, SpreadZ = s.SpreadZ, Trail = s.InnerTrailMode, VertexAlpha = vertexAlpha, Material = val2, MaxParticles = s.InnerMaxParticles }; if ((Object)(object)val2 != (Object)null && (s.EmitterMode != FogEmitterMode.Mesh || !SpawnMeshFog(p, snap, s, color, fogLayer))) { bool flag = false; foreach (FogAnchor anchor in s.Anchors) { if (anchor.Enabled && !(s.Rate <= 0f)) { flag = true; if ((Object)(object)animator == (Object)null) { break; } Transform val5 = BoneFor(animator, anchor.Name); if (!((Object)(object)val5 == (Object)null)) { snap.Fog.Add(SpawnEmitter(((Component)p).transform, val5, anchor.Name, FogLayerKind.Inner, anchor.Radius, new Vector3(anchor.X, anchor.Y, anchor.Z), color, fogLayer, s, snap.MaterialHasColor)); } } } if ((Object)(object)animator == (Object)null && flag) { snap.Fog.Add(SpawnEmitter(((Component)p).transform, ((Component)p).transform, "Root", FogLayerKind.Inner, 0.4f, new Vector3(0f, 1f, 0f), color, fogLayer, s, snap.MaterialHasColor)); } } if ((Object)(object)val3 != (Object)null) { Transform hips = (((Object)(object)animator != (Object)null) ? FindBone(animator, "Hips") : null); snap.Fog.Add(SpawnGroundEmitter(((Component)p).transform, hips, color, s, upper: false, val3, vertexAlpha2, snap.MaterialHasColor)); if ((Object)(object)val4 != (Object)null) { snap.Fog.Add(SpawnGroundEmitter(((Component)p).transform, hips, color, s, upper: true, val4, vertexAlpha3, snap.MaterialHasColor)); } } if ((Object)(object)outer == (Object)null || (Object)(object)animator == (Object)null) { return; } FogOverlapCap overlapCap = s.OuterOverlapCap; bool volume = s.OuterShape == FogOuterShape.Volume; FogLayer fogLayer2 = (s.OuterHasTrail ? OuterLayer(follow: false) : null); FogLayer fogLayer3 = (s.OuterHasFollow ? OuterLayer(follow: true) : null); Vector3 offset = default(Vector3); foreach (string outerAnchor in s.OuterAnchors) { FogAnchor fogAnchor = s.Anchor(outerAnchor); if (fogAnchor == null) { continue; } Transform val6 = BoneFor(animator, fogAnchor.Name); if (!((Object)(object)val6 == (Object)null)) { ((Vector3)(ref offset))..ctor(fogAnchor.X, fogAnchor.Y + s.OuterOffsetY, fogAnchor.Z); if (fogLayer3 != null) { snap.Fog.Add(SpawnEmitter(((Component)p).transform, val6, fogAnchor.Name + " follow", FogLayerKind.Outer, s.OuterFollowRadius, offset, color, fogLayer3, s, snap.MaterialHasColor, "ip_fog_outer_follow")); } if (fogLayer2 != null) { snap.Fog.Add(SpawnEmitter(((Component)p).transform, val6, (fogLayer3 != null) ? (fogAnchor.Name + " trail") : fogAnchor.Name, FogLayerKind.Outer, s.OuterRadius, offset, color, fogLayer2, s, snap.MaterialHasColor)); } } } FogLayer OuterLayer(bool follow) { FogLayer fogLayer4 = new FogLayer { Rate = (follow ? s.OuterFollowRate : s.OuterTrailRate), Size = (follow ? s.OuterFollowSize : s.OuterSize), Lifetime = s.OuterLifetime, SpreadX = 1f, SpreadY = s.OuterSpreadY, SpreadZ = 1f, Trail = ((!follow) ? s.OuterTrailMode : FogTrailMode.Follow), VertexAlpha = outerVertexAlpha, Material = outer, Horizontal = s.OuterHorizontal, Ring = !volume, Volume = volume, Orbital = (follow ? s.OuterFollowOrbitalRadPerSecond : s.OuterOrbitalRadPerSecond), PositionOnly = volume, EvenBurst = follow, RateDistance = (follow ? 0f : s.OuterEffectiveRateDistance), MaxParticles = (follow ? s.OuterFollowMaxParticles : s.OuterTrailMaxParticles) }; if (volume) { fogLayer4.SizeMinFactor = 0.8f; fogLayer4.SizeMaxFactor = 1.2f; fogLayer4.Speed = 0.05f; fogLayer4.Grow = 1.35f; fogLayer4.Burst = (follow ? s.OuterFollowBurstCount : s.OuterTrailBurstCount); fogLayer4.HeightSigma = s.OuterHeightSigma; fogLayer4.GroundOffsetY = s.OuterOffsetY; fogLayer4.Cap = overlapCap; } return fogLayer4; } } private static Transform BoneFor(Animator animator, string anchor) { Transform val = FindBone(animator, anchor); if ((Object)(object)val == (Object)null && _missingBoneWarned.Add(anchor)) { Plugin.Log.LogWarning((object)("veil fog: bone for anchor '" + anchor + "' not found; anchor skipped")); } return val; } private static Material MakeFogMaterial(Material source, Snapshot snap, Color color, float alpha, float emission, out float vertexAlpha) { //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_0012: Expected O, but got Unknown //IL_0012: 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_0018: 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_006d: 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_007a: 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_009a: Unknown result type (might be due to invalid IL or missing references) Material val = new Material(source) { name = "ip_fog_mat" }; color = ClampRgb(color); bool flag = val.HasProperty(ColorId); float num; if (!flag || AlphaMode == FogAlphaMode.Vertex) { num = 1f; vertexAlpha = alpha; } else if (AlphaMode == FogAlphaMode.Material) { num = alpha; vertexAlpha = 1f; } else { num = (vertexAlpha = Mathf.Sqrt(alpha)); } if (flag) { val.SetColor(ColorId, new Color(color.r, color.g, color.b, num)); } if (val.HasProperty(EmissionColorId)) { val.SetColor(EmissionColorId, Emission(color, emission)); } if (val.HasProperty(ZFadeDistanceId)) { val.SetFloat(ZFadeDistanceId, 0.25f); } if (val.HasProperty(CameraFadeMinId) && val.HasProperty(CameraFadeMaxId)) { val.SetFloat(CameraFadeMinId, 0.3f); val.SetFloat(CameraFadeMaxId, 1f); } snap.MaterialHasColor = flag; snap.FogMaterials.Add(val); return val; } private static Color Emission(Color color, float emission) { //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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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_002c: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Clamp01(emission); color = ClampRgb(color); return new Color(color.r * num, color.g * num, color.b * num, 1f); } private static Color ClampRgb(Color c) { //IL_0000: 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_0016: 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) //IL_0027: Unknown result type (might be due to invalid IL or missing references) return new Color(Mathf.Clamp01(c.r), Mathf.Clamp01(c.g), Mathf.Clamp01(c.b), c.a); } private static Color FogColor(FogSettings s) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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_001f: Unknown result type (might be due to invalid IL or missing references) if (!s.DynamicColor) { return ClampRgb(new Color(s.R, s.G, s.B)); } Color fogColor = RenderSettings.fogColor; FogRgb fogRgb = FogRgb.Blend(s.Color, new FogRgb(fogColor.r, fogColor.g, fogColor.b), 0.5f); return ClampRgb(new Color(fogRgb.R, fogRgb.G, fogRgb.B)); } private static void UpdateFogColor(Snapshot snap) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_0038: 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) //IL_0057: 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_0073: 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_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: 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_00c5: 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) //IL_00d3: 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_00f9: Unknown result type (might be due to invalid IL or missing references) if (snap.Fog.Count == 0) { return; } FogSettings fogSettings = Fog[snap.Tier]; if (!fogSettings.DynamicColor) { return; } Color val = FogColor(fogSettings); Color appliedColor = snap.AppliedColor; if (Mathf.Abs(val.r - appliedColor.r) + Mathf.Abs(val.g - appliedColor.g) + Mathf.Abs(val.b - appliedColor.b) < 0.01f) { return; } snap.AppliedColor = val; if (snap.MaterialHasColor) { foreach (Material fogMaterial in snap.FogMaterials) { if (!((Object)(object)fogMaterial == (Object)null)) { float a = fogMaterial.GetColor(ColorId).a; fogMaterial.SetColor(ColorId, new Color(val.r, val.g, val.b, a)); if (fogMaterial.HasProperty(EmissionColorId)) { fogMaterial.SetColor(EmissionColorId, Emission(val, fogSettings.Emission)); } } } return; } foreach (FogEmitter item in snap.Fog) { if (!((Object)(object)item.Ps == (Object)null)) { MainModule main = item.Ps.main; ((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(new Color(val.r, val.g, val.b, item.VertexAlpha)); } } } private static FogEmitter SpawnEmitter(Transform root, Transform bone, string anchor, FogLayerKind kind, float radius, Vector3 offset, Color color, FogLayer layer, FogSettings s, bool materialHasColor, string objectName = null) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //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_006c: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: 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_0266: 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) GameObject val = new GameObject(objectName ?? ((kind == FogLayerKind.Outer) ? "ip_fog_outer" : "ip_fog")); val.SetActive(false); val.transform.SetParent(root, false); FogAnchorFollower fogAnchorFollower = val.AddComponent<FogAnchorFollower>(); fogAnchorFollower.Bone = bone; fogAnchorFollower.Root = root; fogAnchorFollower.Offset = offset; fogAnchorFollower.PositionOnly = layer.PositionOnly; fogAnchorFollower.SpawnedAt = Time.time; fogAnchorFollower.Snap(); ParticleSystem val2 = ConfigureSystem(val, color, layer, s, materialHasColor); ShapeModule shape = val2.shape; ((ShapeModule)(ref shape)).enabled = true; if (layer.Ring) { ((ShapeModule)(ref shape)).shapeType = (ParticleSystemShapeType)10; ((ShapeModule)(ref shape)).radius = Mathf.Max(0.001f, radius); ((ShapeModule)(ref shape)).radiusThickness = 0.25f; ((ShapeModule)(ref shape)).arc = 360f; ((ShapeModule)(ref shape)).rotation = new Vector3(90f, 0f, 0f); ((ShapeModule)(ref shape)).scale = Vector3.one; ((ShapeModule)(ref shape)).randomPositionAmount = Mathf.Max(0f, layer.SpreadY * radius); } else if (layer.Volume) { ((ShapeModule)(ref shape)).shapeType = (ParticleSystemShapeType)0; ((ShapeModule)(ref shape)).radius = Mathf.Max(0.001f, radius); ((ShapeModule)(ref shape)).radiusThickness = 1f; ((ShapeModule)(ref shape)).scale = Vector3.one; ((ShapeModule)(ref shape)).enabled = false; fogAnchorFollower.Volume = new FogVolumeEmission { Rate = Mathf.Max(0f, layer.Rate), RateDistance = ((layer.Trail == FogTrailMode.Trail) ? Mathf.Max(0f, layer.RateDistance) : 0f), Radius = Mathf.Max(0f, radius), Sigma = layer.HeightSigma, OffsetY = layer.GroundOffsetY, Size = layer.Size, SizeMinFactor = layer.SizeMinFactor, SizeMaxFactor = layer.SizeMaxFactor, Speed = (float.IsNaN(layer.Speed) ? s.Speed : layer.Speed), World = (layer.Trail == FogTrailMode.Trail), Cap = layer.Cap }; } else { ((ShapeModule)(ref shape)).shapeType = (ParticleSystemShapeType)0; ((ShapeModule)(ref shape)).radius = Mathf.Max(0.001f, radius); ((ShapeModule)(ref shape)).scale = new Vector3(layer.SpreadX, layer.SpreadY, layer.SpreadZ); } val.SetActive(true); val2.Play(); fogAnchorFollower.Ps = val2; int burst = ((layer.Volume && layer.Burst > 0 && fogAnchorFollower.Volume != null) ? EmitBurst(val2, layer.Burst, layer.EvenBurst && layer.Trail == FogTrailMode.Follow, fogAnchorFollower.Volume, val.transform, root) : 0); return new FogEmitter { Go = val, Ps = val2, Anchor = anchor, Layer = kind, VertexAlpha = layer.VertexAlpha, Burst = burst }; } private static int EmitBurst(ParticleSystem ps, int count, bool even, FogVolumeEmissi