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 CrimsonBlood v0.7.9
CrimsonBlood.dll
Decompiled 2 months 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.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; using UnityEngine.Networking; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("0.0.0.0")] namespace CrimsonBlood; [BepInPlugin("marc.valheim.crimsonblood", "Crimson Blood", "0.9.0.0")] [BepInProcess("valheim.exe")] public sealed class CrimsonBloodPlugin : BaseUnityPlugin { private enum PlayerHitSoundKind { None, Sword, Axe, Arrow } private sealed class BleedState { public Character Target; public Vector3 LastDripPosition; public float LastDripTime; public float Intensity; public bool IsPlayer; public Color Color; } public const string PluginGuid = "marc.valheim.crimsonblood"; public const string PluginName = "Crimson Blood"; public const string PluginVersion = "0.9.0.0"; private const string YoureInjuredGuid = "com.marccunningham.youreinjured"; private const float MinimumMeaningfulHealthLoss = 0.01f; private const float MinimumDirectionMagnitude = 0.0001f; private const int MinimumPoolSize = 4; private const int MaximumPoolSize = 240; private const int MaximumPersistentSplatPoolSize = 512; private const double ValheimWorldSecondsPerDay = 86400.0; private const float DefaultValheimRealSecondsPerDay = 1800f; private const float MinimumVisualDistance = 5f; private const float MaximumVisualDistance = 150f; private const float MinimumImpactCooldown = 0.03f; private const float MaximumImpactCooldown = 0.75f; internal static CrimsonBloodPlugin Instance; private ConfigEntry<bool> enabledConfig; private ConfigEntry<KeyCode> diagnosticKey; private ConfigEntry<KeyCode> clearDiagnosticsKey; private ConfigEntry<float> visualDistance; private ConfigEntry<float> dynamicEffectRenderDistance; private ConfigEntry<float> surfaceSplatRenderDistance; private ConfigEntry<float> minimumHealthLossForVisual; private ConfigEntry<float> perTargetCooldown; private ConfigEntry<bool> useExternalAssetTextures; private ConfigEntry<string> assetRootFolder; private ConfigEntry<bool> useHighDefinitionGroundSplats; private ConfigEntry<int> maximumBurstTextures; private ConfigEntry<int> maximumTrailTextures; private ConfigEntry<int> maximumGroundSplatTextures; private ConfigEntry<int> burstPoolSize; private ConfigEntry<int> streakPoolSize; private ConfigEntry<int> dropletPoolSize; private ConfigEntry<int> splatPoolSize; private ConfigEntry<int> maximumPersistentSurfaceSplats; private ConfigEntry<int> maximumDropletsPerHit; private ConfigEntry<int> maximumStreaksPerHeavyHit; private ConfigEntry<int> maximumBurstsPerHeavyHit; private ConfigEntry<float> dropletGravity; private ConfigEntry<float> dropletLifetime; private ConfigEntry<float> streakLifetime; private ConfigEntry<float> burstLifetime; private ConfigEntry<float> surfaceSplatLifetimeInGameDays; private ConfigEntry<float> minimumSplatHealthLoss; private ConfigEntry<bool> createImmediateImpactSplats; private ConfigEntry<bool> allowWallMarks; private ConfigEntry<bool> groundOnlySurfaceMarks; private ConfigEntry<bool> createFallbackSplatsWhenAirborne; private ConfigEntry<float> surfaceOffset; private ConfigEntry<float> splatSettleSeconds; private ConfigEntry<bool> allowPlayerImpactVisuals; private ConfigEntry<bool> suppressPlayerImpactsWhenYoureInjuredLoaded; private ConfigEntry<bool> disableVanillaBloodEffects; private ConfigEntry<string> vanillaBloodEffectTokens; private ConfigEntry<bool> recordVanillaEffectRoots; private ConfigEntry<float> vanillaEffectWindowSeconds; private ConfigEntry<float> immediateWallProbeDistance; private ConfigEntry<float> maxImmediateSplatSize; private ConfigEntry<bool> useGroundedLethalFinish; private ConfigEntry<bool> logVisualEvents; private ConfigEntry<float> bluntBloodMultiplier; private ConfigEntry<int> bluntMaximumDropletsPerHit; private ConfigEntry<float> bluntBurstSizeMultiplier; private ConfigEntry<float> bluntSplatSizeMultiplier; private ConfigEntry<float> bluntLifetimeMultiplier; private ConfigEntry<bool> enableWoundBodyMarks; private ConfigEntry<float> bodyMarkLifetimeSeconds; private ConfigEntry<int> maxBodyMarksPerTarget; private ConfigEntry<bool> enableBleedingTrails; private ConfigEntry<float> trailMinimumHealthLoss; private ConfigEntry<float> trailDripDistance; private ConfigEntry<float> trailDripInterval; private ConfigEntry<float> trailDripSizeMultiplier; private ConfigEntry<int> maxBleedingActors; private ConfigEntry<bool> enablePlayerBleedDamage; private ConfigEntry<float> playerBleedDamagePerTick; private ConfigEntry<float> playerBleedTickSeconds; private ConfigEntry<bool> playerBleedCanKill; private float nextPlayerBleedDamageTime; private ConfigEntry<KeyCode> bandageKey; private ConfigEntry<bool> requireBandageMaterial; private ConfigEntry<string> bandageItemSharedName; private ConfigEntry<int> bandageItemCost; private ConfigEntry<bool> enableBandageAudio; private ConfigEntry<float> bandageAudioVolume; private ConfigEntry<string> bandageAudioFile; private ConfigEntry<bool> enablePlayerHitSounds; private ConfigEntry<float> playerHitSoundVolume; private ConfigEntry<string> swordHitSoundFile; private ConfigEntry<string> axeHitSoundFile; private ConfigEntry<string> arrowHitSoundFile; private Harmony harmony; private Transform visualRoot; private string pluginDirectory; private string activeAssetDirectory; private Material quadMaterial; private Texture2D fallbackDropletTexture; private Texture2D fallbackBurstTexture; private Texture2D fallbackSplatTexture; private Texture2D fallbackTrailTexture; private readonly List<Texture2D> burstTextures = new List<Texture2D>(); private readonly List<Texture2D> dropletTextures = new List<Texture2D>(); private readonly List<Texture2D> splatTextures = new List<Texture2D>(); private readonly List<Texture2D> trailTextures = new List<Texture2D>(); private readonly List<Texture2D> ownedTextures = new List<Texture2D>(); private readonly List<Material> ownedMaterials = new List<Material>(); private readonly List<CrimsonBloodBurst> burstPool = new List<CrimsonBloodBurst>(); private readonly List<CrimsonBloodStreak> streakPool = new List<CrimsonBloodStreak>(); private readonly List<CrimsonBloodDroplet> dropletPool = new List<CrimsonBloodDroplet>(); private readonly List<CrimsonBloodSplat> splatPool = new List<CrimsonBloodSplat>(); private readonly List<CrimsonBloodBodyMark> bodyMarkPool = new List<CrimsonBloodBodyMark>(); private readonly Dictionary<int, float> nextAllowedImpactTime = new Dictionary<int, float>(); private readonly Dictionary<Type, FieldInfo[]> damageFieldCache = new Dictionary<Type, FieldInfo[]>(); private readonly Dictionary<Character, BleedState> bleedingActors = new Dictionary<Character, BleedState>(); private readonly List<Character> bleedingActorsToRemove = new List<Character>(); private readonly Dictionary<Character, List<CrimsonBloodBodyMark>> bodyMarksByTarget = new Dictionary<Character, List<CrimsonBloodBodyMark>>(); private readonly Queue<string> recentEvents = new Queue<string>(); private readonly Queue<string> recentVanillaEffectRoots = new Queue<string>(); private readonly RaycastHit[] immediateSurfaceHits = (RaycastHit[])(object)new RaycastHit[16]; private Camera cachedMainCamera; private AudioSource bandageAudioSource; private AudioClip bandageAudioClip; private AudioClip swordHitAudioClip; private AudioClip axeHitAudioClip; private AudioClip arrowHitAudioClip; private string loadedBandageAudioPath; private float nextCameraSearchTime; private float nextCooldownTrimTime; private float nextBleedUpdateTime; private float nextPlayerHitSoundTime; private bool bandageAudioLoadAttempted; private bool bandageAudioLoadInProgress; private bool playerHitAudioLoadAttempted; private bool playerHitAudioLoadInProgress; private int applyDamageHookCalls; private int observedDamageCalls; private int realHealthLossEvents; private int zeroHealthLossEvents; private int visualEvents; private int burstsSpawned; private int streaksSpawned; private int dropletsSpawned; private int splatsSpawned; private int recycledBursts; private int recycledStreaks; private int recycledDroplets; private int recycledSplats; private int suppressedPlayerImpactEvents; private int cooldownSkippedEvents; private int distanceSkippedEvents; private int surfaceLandingEvents; private int fallbackLandingEvents; private int fallbackSplatsDiscarded; private int characterSurfaceSkips; private int rigidbodySurfaceSkips; private int invalidSurfaceSkips; private int suppressedVanillaBloodEffects; private int observedVanillaEffectRoots; private int unmatchedVanillaEffectRoots; private int mutedVanillaRenderers; private int groundedLethalFinishes; private int destroyedPoolEntriesRecovered; private int bodyMarksSpawned; private int trailDripsSpawned; private int bleedingActorsStarted; private int bleedingActorsPruned; private int bandagesApplied; private int playerBleedDamageTicks; private int bandageAttemptsFailed; private int playerHitSoundsPlayed; private int playerHitSoundsSkipped; private float vanillaEffectWindowUntil; private Vector3 vanillaEffectWindowCenter; private bool externalAssetsLoaded; private string assetLoadSummary = "Not loaded yet"; private string lastEvent = "none"; internal float DropletGravityMultiplier { get { if (dropletGravity != null) { return Mathf.Clamp(dropletGravity.Value, 0.1f, 6f); } return 1f; } } internal float SurfaceOffset { get { if (surfaceOffset != null) { return Mathf.Clamp(surfaceOffset.Value, 0.001f, 0.05f); } return 0.006f; } } internal float SplatSettleSeconds { get { if (splatSettleSeconds != null) { return Mathf.Clamp(splatSettleSeconds.Value, 0.05f, 0.5f); } return 0.14f; } } internal float SurfaceSplatLifetimeInGameDays { get { if (surfaceSplatLifetimeInGameDays != null) { return Mathf.Clamp(surfaceSplatLifetimeInGameDays.Value, 0.05f, 7f); } return 2f; } } internal float DynamicEffectRenderDistance { get { if (dynamicEffectRenderDistance != null) { return Mathf.Clamp(dynamicEffectRenderDistance.Value, 6f, 60f); } return 24f; } } internal float SurfaceSplatRenderDistance { get { if (surfaceSplatRenderDistance != null) { return Mathf.Clamp(surfaceSplatRenderDistance.Value, 4f, 50f); } return 18f; } } internal bool DisableVanillaBloodEffects { get { if (disableVanillaBloodEffects != null) { return disableVanillaBloodEffects.Value; } return false; } } internal bool UseGroundedLethalFinish { get { if (useGroundedLethalFinish != null) { return useGroundedLethalFinish.Value; } return true; } } internal bool CreateFallbackSplatsWhenAirborne { get { if (createFallbackSplatsWhenAirborne != null) { return createFallbackSplatsWhenAirborne.Value; } return false; } } internal bool AllowWallMarks { get { if (allowWallMarks != null) { return allowWallMarks.Value; } return false; } } internal Camera RenderCamera { get { if ((Object)(object)cachedMainCamera != (Object)null && ((Behaviour)cachedMainCamera).isActiveAndEnabled) { return cachedMainCamera; } if (Time.unscaledTime >= nextCameraSearchTime) { nextCameraSearchTime = Time.unscaledTime + 0.5f; cachedMainCamera = Camera.main; } return cachedMainCamera; } } internal bool TryGetWorldTimeSeconds(out double worldTimeSeconds) { worldTimeSeconds = 0.0; try { if ((Object)(object)ZNet.instance == (Object)null) { return false; } double timeSeconds = ZNet.instance.GetTimeSeconds(); if (double.IsNaN(timeSeconds) || double.IsInfinity(timeSeconds) || timeSeconds < 0.0) { return false; } worldTimeSeconds = timeSeconds; return true; } catch { return false; } } internal bool IsCharacterSurface(Collider collider) { if ((Object)(object)collider != (Object)null) { return (Object)(object)((Component)collider).GetComponentInParent<Character>() != (Object)null; } return false; } internal bool IsRejectedBloodSurface(Collider collider) { if ((Object)(object)collider == (Object)null) { return true; } if (IsCharacterSurface(collider)) { characterSurfaceSkips++; return true; } if ((Object)(object)collider.attachedRigidbody != (Object)null) { rigidbodySurfaceSkips++; return true; } Transform val = ((Component)collider).transform; int num = 0; while ((Object)(object)val != (Object)null && num < 8) { string text = ((Object)val).name ?? string.Empty; if (text.IndexOf("ragdoll", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("corpse", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("character", StringComparison.OrdinalIgnoreCase) >= 0) { invalidSurfaceSkips++; return true; } num++; val = val.parent; } return false; } internal bool IsValidBloodSurfaceNormal(Vector3 normal) { //IL_0019: 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) if (!AllowWallMarks) { return normal.y >= 0.55f; } return normal.y >= -0.35f; } internal void RecordCharacterSurfaceSkip() { characterSurfaceSkips++; } internal void RecordAirborneFallbackDiscarded() { fallbackSplatsDiscarded++; } internal void BeginVanillaEffectWindow(Character target) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (DisableVanillaBloodEffects && !((Object)(object)target == (Object)null)) { vanillaEffectWindowCenter = target.GetCenterPoint(); float num = ((vanillaEffectWindowSeconds == null) ? 1.25f : Mathf.Clamp(vanillaEffectWindowSeconds.Value, 0.25f, 3f)); vanillaEffectWindowUntil = Mathf.Max(vanillaEffectWindowUntil, Time.time + num); } } internal void OnVanillaEffectsCreated(GameObject[] createdEffects) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (!DisableVanillaBloodEffects || createdEffects == null || createdEffects.Length == 0 || Time.time > vanillaEffectWindowUntil) { return; } foreach (GameObject val in createdEffects) { if ((Object)(object)val == (Object)null) { continue; } Vector3 val2 = val.transform.position - vanillaEffectWindowCenter; if (((Vector3)(ref val2)).sqrMagnitude > 625f) { continue; } string text = BuildVanillaEffectSignature(val); observedVanillaEffectRoots++; if (EffectSignatureMatchesVanillaBlood(text)) { MuteVanillaBloodVisuals(val); suppressedVanillaBloodEffects++; RememberVanillaEffectRoot("MUTED " + text); continue; } unmatchedVanillaEffectRoots++; if (recordVanillaEffectRoots != null && recordVanillaEffectRoots.Value) { RememberVanillaEffectRoot("OBSERVED " + text); } } } private void MuteVanillaBloodVisuals(GameObject root) { if ((Object)(object)root == (Object)null) { return; } Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>(true); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { try { val.enabled = false; mutedVanillaRenderers++; } catch { } } } } private bool EffectSignatureMatchesVanillaBlood(string signature) { if (string.IsNullOrEmpty(signature)) { return false; } string[] array = ((vanillaBloodEffectTokens != null) ? vanillaBloodEffectTokens.Value : "blood,bleed,gore,splatter").Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); string text = signature.ToLowerInvariant(); for (int i = 0; i < array.Length; i++) { string value = array[i].Trim().ToLowerInvariant(); if (!string.IsNullOrEmpty(value) && text.Contains(value)) { return true; } } return false; } private static string BuildVanillaEffectSignature(GameObject root) { if ((Object)(object)root == (Object)null) { return "<destroyed>"; } string text = ((Object)root).name ?? "unnamed"; Transform transform = root.transform; for (int i = 0; i < transform.childCount && i < 24; i++) { Transform child = transform.GetChild(i); if ((Object)(object)child == (Object)null) { continue; } text = text + " / " + ((Object)child).name; for (int j = 0; j < child.childCount && j < 6; j++) { Transform child2 = child.GetChild(j); if ((Object)(object)child2 != (Object)null) { text = text + " / " + ((Object)child2).name; } } } return text; } private void RememberVanillaEffectRoot(string line) { while (recentVanillaEffectRoots.Count >= 24) { recentVanillaEffectRoots.Dequeue(); } recentVanillaEffectRoots.Enqueue(line); } internal bool ShouldRenderEffectAt(Vector3 worldPosition, bool isSurfaceMark) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) Camera renderCamera = RenderCamera; Vector3 val = (((Object)(object)renderCamera != (Object)null) ? ((Component)renderCamera).transform.position : (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Component)Player.m_localPlayer).transform.position : Vector3.zero)); float num = (isSurfaceMark ? SurfaceSplatRenderDistance : DynamicEffectRenderDistance); Vector3 val2 = worldPosition - val; return ((Vector3)(ref val2)).sqrMagnitude <= num * num; } private void Awake() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown Instance = this; pluginDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); BindConfig(); harmony = new Harmony("marc.valheim.crimsonblood"); harmony.PatchAll(typeof(CrimsonBloodPlugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Crimson Blood 0.9.0.0 loaded. F11 writes diagnostics; F12 clears recent events."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Asset-driven mode expects an assets folder beside CrimsonBlood.dll. If it is missing, safe procedural fallback textures are used."); } private void OnDestroy() { if (harmony != null) { harmony.UnpatchSelf(); harmony = null; } DestroyVisualSystem(); nextAllowedImpactTime.Clear(); damageFieldCache.Clear(); bleedingActors.Clear(); bleedingActorsToRemove.Clear(); bodyMarksByTarget.Clear(); recentEvents.Clear(); recentVanillaEffectRoots.Clear(); if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private void Update() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (Input.GetKeyDown(diagnosticKey.Value)) { WriteDiagnostics(); } if (Input.GetKeyDown(clearDiagnosticsKey.Value)) { recentEvents.Clear(); recentVanillaEffectRoots.Clear(); lastEvent = "none"; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Crimson Blood: cleared recent event diagnostics."); } if ((Object)(object)Player.m_localPlayer != (Object)null) { EnsureVisualSystem(); } HandleBandageInput(); UpdateBleedingTrails(); UpdatePlayerBleedDamage(); if (Time.time >= nextCooldownTrimTime) { nextCooldownTrimTime = Time.time + 20f; TrimExpiredCooldowns(); } } private void BindConfig() { enabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Master", "Enabled", true, "Turns Crimson Blood's local visual layer on or off. It never changes combat balance."); diagnosticKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("0 - Master", "DiagnosticKey", (KeyCode)292, "Writes Crimson Blood diagnostic information to BepInEx/LogOutput.log."); clearDiagnosticsKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("0 - Master", "ClearDiagnosticsKey", (KeyCode)293, "Clears Crimson Blood's recent diagnostic event list."); visualDistance = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Safety", "VisualDistanceMetres", 80f, "Maximum distance from the local player at which combat blood visuals are created."); dynamicEffectRenderDistance = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Safety", "DynamicEffectRenderDistanceMetres", 24f, "Maximum distance from the camera at which airborne bursts, streaks, and droplets remain visible."); surfaceSplatRenderDistance = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Safety", "SurfaceSplatRenderDistanceMetres", 18f, "Maximum distance from the camera at which surface blood marks remain visible."); minimumHealthLossForVisual = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Safety", "MinimumActualHealthLoss", 0.1f, "Actual health lost after armour, blocking and resistances required before a visual may spawn."); perTargetCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Safety", "PerTargetImpactCooldownSeconds", 0.06f, "Stops rapid repeated damage ticks from producing excessive visual bursts."); useExternalAssetTextures = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Asset Loading", "UseExternalAssetTextures", true, "Loads supplied transparent PNG effects from the assets folder beside CrimsonBlood.dll."); assetRootFolder = ((BaseUnityPlugin)this).Config.Bind<string>("2 - Asset Loading", "AssetRootFolder", "assets", "Folder beside CrimsonBlood.dll that contains sprays, trails, and ground_splats."); useHighDefinitionGroundSplats = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Asset Loading", "UseHighDefinitionGroundSplats", false, "Uses assets/ground_splats/kenney_512 if you copied it beside the DLL. This uses more video memory than the default 256px set."); maximumBurstTextures = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Asset Loading", "MaximumBurstTextures", 6, "Maximum number of splash-frame textures loaded from assets/sprays/blood_splash_frames."); maximumTrailTextures = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Asset Loading", "MaximumTrailTextures", 8, "Maximum number of trail/droplet textures loaded from assets/trails."); maximumGroundSplatTextures = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Asset Loading", "MaximumGroundSplatTextures", 36, "Maximum number of ground-splat textures loaded. 36 default 256px textures are safe for this first asset build."); burstPoolSize = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Safety Pools", "ImpactBurstPoolSize", 20, "Pre-warmed maximum number of animated impact burst sprites."); streakPoolSize = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Safety Pools", "PressureStreakPoolSize", 22, "Pre-warmed maximum number of pressure spray streaks."); dropletPoolSize = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Safety Pools", "DropletPoolSize", 150, "Pre-warmed maximum number of moving droplets."); splatPoolSize = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Safety Pools", "SurfaceSplatPoolSize", 160, "Pre-warmed number of temporary blood splats. The pool can grow safely while two-day marks are still active."); maximumPersistentSurfaceSplats = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Safety Pools", "MaximumPersistentSurfaceSplats", 480, "Maximum number of surface blood marks that may coexist before the oldest mark is reused. Higher values preserve more two-day blood trails but use more performance."); maximumDropletsPerHit = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Look", "MaximumDropletsPerHit", 26, "Upper safety cap for liquid droplets created by one strong hit."); maximumStreaksPerHeavyHit = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Look", "MaximumPressureStreaksPerHeavyHit", 6, "Upper safety cap for streaks on stronger hits."); maximumBurstsPerHeavyHit = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Look", "MaximumAnimatedBurstsPerHeavyHit", 3, "Upper safety cap for animated splash bursts on stronger hits."); dropletGravity = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "DropletGravityMultiplier", 1.85f, "Gravity multiplier for airborne droplets. Higher values make them fall and hit surfaces sooner."); dropletLifetime = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "DropletLifetimeSeconds", 1.2f, "Maximum airborne lifetime before a droplet fades or tries a final nearby splat."); streakLifetime = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "PressureStreakLifetimeSeconds", 0.18f, "Lifetime of directional pressure spray streaks."); burstLifetime = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "ImpactBurstLifetimeSeconds", 0.16f, "Lifetime of animated impact burst sprites."); surfaceSplatLifetimeInGameDays = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "SurfaceSplatLifetimeInGameDays", 2f, "How many full Valheim in-game days ground and structure blood remains before it fades. Default: 2 days."); minimumSplatHealthLoss = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "MinimumHealthLossForImmediateImpactSplat", 0.85f, "Health loss required before an immediate blood mark may appear below/near the impact."); createImmediateImpactSplats = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Look", "CreateImmediateImpactSplats", true, "Places one small wet mark near a meaningful hit where a suitable surface exists."); allowWallMarks = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Look", "AllowWallAndSteepSurfaceBloodMarks", true, "Allows blood marks on static walls, wooden structures, steep rocks, floors, and terrain."); groundOnlySurfaceMarks = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Look", "GroundOnlySurfaceMarks", false, "Legacy compatibility setting. v0.7.5 ignores this value so wall and steep-surface marks work when AllowWallAndSteepSurfaceBloodMarks is true."); immediateWallProbeDistance = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "ImmediateWallProbeDistanceMetres", 1.35f, "How far a fresh hit checks behind and around the wound for a nearby static wall or steep rock to receive blood."); maxImmediateSplatSize = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "MaxImmediateSplatSize", 0.45f, "Hard cap on the size of an immediate impact/ground/wall blood mark. Lower this if kill blood looks like a giant slab. Range 0.10-0.80."); createFallbackSplatsWhenAirborne = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Look", "CreateFallbackSplatsWhenAirborne", false, "When false, droplets that never reach a valid ground surface simply fade instead of creating a late fallback blood mark in the air."); surfaceOffset = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "SurfaceOffset", 0.006f, "Small offset that keeps a blood mark just above its surface and avoids flickering."); splatSettleSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "SurfaceSplatSettleSeconds", 0.14f, "Real-time seconds a new blood mark takes to spread to its final size. This is independent of splat lifetime."); bluntBloodMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "BluntHeavyHitBloodMultiplier", 1.85f, "Extra amount of blood created by strong blunt hits such as maces and clubs. Affects heavy hits only."); bluntMaximumDropletsPerHit = ((BaseUnityPlugin)this).Config.Bind<int>("4 - Look", "MaximumDropletsPerHeavyBluntHit", 30, "Safety cap used only for strong blunt hits. Lets mace and club impacts be fuller without raising every weapon type."); bluntBurstSizeMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "BluntHeavyHitBurstSizeMultiplier", 1.6f, "Size multiplier for animated impact bursts created by strong blunt hits."); bluntSplatSizeMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "BluntHeavyHitSplatSizeMultiplier", 1.45f, "Size multiplier for blood marks created by strong blunt hits."); bluntLifetimeMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("4 - Look", "BluntHeavyHitLifetimeMultiplier", 1.38f, "Makes blunt-hit bursts, droplets, and streaks stay visible slightly longer."); allowPlayerImpactVisuals = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - Player Compatibility", "AllowPlayerImpactVisuals", true, "Allows Crimson Blood to create impact visuals when the local player takes real damage."); suppressPlayerImpactsWhenYoureInjuredLoaded = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - Player Compatibility", "PreventDuplicatePlayerHitBloodWhenYoureInjuredLoaded", true, "Compatibility setting retained for existing configs. When You're Injured is installed, Crimson Blood now automatically leaves local-player blood to that mod so only one player blood visual system can run."); disableVanillaBloodEffects = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - Player Compatibility", "DisableVanillaBloodEffects", false, "DEFAULT OFF. When true, Crimson Blood hooks EffectList.Create to hide Valheim's own blood effect near a hit. This hook scans spawned effects and disables their renderers, which can add overhead during combat and has been associated with ZNetScene cleanup errors on some setups. Leave OFF unless you specifically need vanilla blood hidden and accept the cost. To remove darker blood from You're Injured, disable that mod's 'Visual Bleeding' instead."); vanillaBloodEffectTokens = ((BaseUnityPlugin)this).Config.Bind<string>("5 - Player Compatibility", "VanillaBloodEffectTokens", "blood,bleed,gore,splatter", "Name fragments used to identify actual vanilla effect GameObjects created during a hit. Unmatched roots appear in F11 diagnostics."); recordVanillaEffectRoots = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - Player Compatibility", "RecordVanillaEffectRootsAroundHits", true, "Records EffectList-created root names near a real hit. Use F11 results to identify any remaining vanilla blood effect exactly."); vanillaEffectWindowSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("5 - Player Compatibility", "VanillaEffectWindowSeconds", 1.25f, "Seconds after a damage event that newly created EffectList roots are inspected for vanilla blood suppression."); useGroundedLethalFinish = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - Player Compatibility", "UseGroundedLethalFinish", true, "On a lethal hit, replaces the hanging animated impact burst with a grounded death spill and falling droplets."); enableWoundBodyMarks = ((BaseUnityPlugin)this).Config.Bind<bool>("6 - Hit Blood, Trails & Bandage", "EnableWoundBodyMarks", true, "Attach short-lived blood marks to the creature/player at the exact hit point."); bodyMarkLifetimeSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("6 - Hit Blood, Trails & Bandage", "BodyMarkLifetimeSeconds", 180f, "Real-time seconds body marks remain attached before fading. They also disappear if the target despawns."); maxBodyMarksPerTarget = ((BaseUnityPlugin)this).Config.Bind<int>("6 - Hit Blood, Trails & Bandage", "MaxBodyMarksPerTarget", 6, "Maximum attached wound marks per creature/player before the oldest mark is recycled."); enableBleedingTrails = ((BaseUnityPlugin)this).Config.Bind<bool>("6 - Hit Blood, Trails & Bandage", "EnableBleedingTrails", true, "Wounded creatures bleed until death/despawn. The local player bleeds until bandaged."); trailMinimumHealthLoss = ((BaseUnityPlugin)this).Config.Bind<float>("6 - Hit Blood, Trails & Bandage", "TrailMinimumHealthLoss", 2f, "Actual health loss required before a target starts leaving a bleeding trail."); trailDripDistance = ((BaseUnityPlugin)this).Config.Bind<float>("6 - Hit Blood, Trails & Bandage", "TrailDripDistance", 1.2f, "Meters a bleeding actor must move before another trail drip is emitted."); trailDripInterval = ((BaseUnityPlugin)this).Config.Bind<float>("6 - Hit Blood, Trails & Bandage", "TrailDripInterval", 0.6f, "Maximum seconds between trail-drip checks while a bleeding actor is visible."); trailDripSizeMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("6 - Hit Blood, Trails & Bandage", "TrailDripSizeMultiplier", 0.55f, "Size multiplier for trail drips relative to impact splats."); maxBleedingActors = ((BaseUnityPlugin)this).Config.Bind<int>("6 - Hit Blood, Trails & Bandage", "MaxBleedingActors", 32, "Maximum simultaneously tracked bleeding actors. Existing bleed states continue when the cap is reached."); enablePlayerBleedDamage = ((BaseUnityPlugin)this).Config.Bind<bool>("6 - Hit Blood, Trails & Bandage", "EnablePlayerBleedDamage", true, "When true, the local player loses health over time while bleeding (until bandaged). Gives bandages a real purpose. Cosmetic-only for creatures; this affects ONLY the local player."); playerBleedDamagePerTick = ((BaseUnityPlugin)this).Config.Bind<float>("6 - Hit Blood, Trails & Bandage", "PlayerBleedDamagePerTick", 0.5f, "Health points removed from the local player each bleed tick. Default 0.5."); playerBleedTickSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("6 - Hit Blood, Trails & Bandage", "PlayerBleedTickSeconds", 2f, "Seconds between player bleed-damage ticks. Default 2.0 (so 0.5 HP every 2s = 0.25 HP/sec)."); playerBleedCanKill = ((BaseUnityPlugin)this).Config.Bind<bool>("6 - Hit Blood, Trails & Bandage", "PlayerBleedCanKill", true, "When true, bleeding can reduce the player to 0 and kill them if never bandaged. When false, bleed damage stops at 1 HP (never lethal on its own)."); bandageKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("6 - Hit Blood, Trails & Bandage", "BandageKey", (KeyCode)103, "Key the local player presses to bandage and stop their cosmetic bleeding trail."); requireBandageMaterial = ((BaseUnityPlugin)this).Config.Bind<bool>("6 - Hit Blood, Trails & Bandage", "RequireBandageMaterial", true, "When true, bandaging consumes the configured inventory item."); bandageItemSharedName = ((BaseUnityPlugin)this).Config.Bind<string>("6 - Hit Blood, Trails & Bandage", "BandageItemSharedName", "$item_leatherscraps", "Shared item name consumed when bandaging. Default is Leather Scraps."); bandageItemCost = ((BaseUnityPlugin)this).Config.Bind<int>("6 - Hit Blood, Trails & Bandage", "BandageItemCost", 1, "Number of configured items consumed when bandaging."); enableBandageAudio = ((BaseUnityPlugin)this).Config.Bind<bool>("6 - Hit Blood, Trails & Bandage", "EnableBandageAudio", true, "Play a local UI-style bandage sound when bleeding stops."); bandageAudioVolume = ((BaseUnityPlugin)this).Config.Bind<float>("6 - Hit Blood, Trails & Bandage", "BandageAudioVolume", 0.9f, "Volume for the local bandage sound."); bandageAudioFile = ((BaseUnityPlugin)this).Config.Bind<string>("6 - Hit Blood, Trails & Bandage", "BandageAudioFile", "bandage.mp3", "Audio file under assets/audio beside the plugin DLL."); enablePlayerHitSounds = ((BaseUnityPlugin)this).Config.Bind<bool>("6 - Hit Blood, Trails & Bandage", "EnablePlayerHitSounds", true, "Play local weapon-specific impact sounds only when the local player is hit by a sword, axe, or arrow."); playerHitSoundVolume = ((BaseUnityPlugin)this).Config.Bind<float>("6 - Hit Blood, Trails & Bandage", "PlayerHitSoundVolume", 0.9f, "Volume for local player-only weapon hit sounds."); swordHitSoundFile = ((BaseUnityPlugin)this).Config.Bind<string>("6 - Hit Blood, Trails & Bandage", "SwordHitSoundFile", "sword_hit.mp3", "Sword impact audio file under assets/audio beside the plugin DLL."); axeHitSoundFile = ((BaseUnityPlugin)this).Config.Bind<string>("6 - Hit Blood, Trails & Bandage", "AxeHitSoundFile", "axe_hit.mp3", "Axe impact audio file under assets/audio beside the plugin DLL."); arrowHitSoundFile = ((BaseUnityPlugin)this).Config.Bind<string>("6 - Hit Blood, Trails & Bandage", "ArrowHitSoundFile", "arrow_hit.mp3", "Arrow impact audio file under assets/audio beside the plugin DLL."); logVisualEvents = ((BaseUnityPlugin)this).Config.Bind<bool>("7 - Debug", "LogVisualEvents", false, "Writes every spawned visual event to LogOutput.log. Leave false outside testing."); } internal void RecordHookCall() { applyDamageHookCalls++; } internal bool ShouldObserve(Character target) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_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) if (!enabledConfig.Value || (Object)(object)target == (Object)null) { return false; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)target == (Object)(object)localPlayer) { return true; } if ((Object)(object)localPlayer == (Object)null) { return false; } float num = Mathf.Clamp(visualDistance.Value, 5f, 150f); Vector3 val = ((Component)target).transform.position - ((Component)localPlayer).transform.position; return ((Vector3)(ref val)).sqrMagnitude <= num * num; } internal void RecordDamage(Character target, HitData hit, float healthBefore) { //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) if (!enabledConfig.Value || (Object)(object)target == (Object)null) { return; } observedDamageCalls++; float health = target.GetHealth(); float num = Mathf.Max(0f, healthBefore - health); if (num < 0.01f) { zeroHealthLossEvents++; return; } realHealthLossEvents++; if ((Object)(object)target == (Object)(object)Player.m_localPlayer) { TryPlayPlayerHitSound(hit); } if (num < Mathf.Max(0.01f, minimumHealthLossForVisual.Value)) { RememberEvent("SKIP threshold target=" + GetTargetName(target) + " loss=" + num.ToString("0.##")); return; } if (!IsWithinVisualRange(target)) { distanceSkippedEvents++; return; } if ((Object)(object)target == (Object)(object)Player.m_localPlayer && !CanSpawnLocalPlayerImpact()) { suppressedPlayerImpactEvents++; RememberEvent("SKIP player-compatibility loss=" + num.ToString("0.##")); return; } int instanceID = ((Object)target).GetInstanceID(); if (nextAllowedImpactTime.TryGetValue(instanceID, out var value) && Time.time < value) { cooldownSkippedEvents++; return; } nextAllowedImpactTime[instanceID] = Time.time + Mathf.Clamp(perTargetCooldown.Value, 0.03f, 0.75f); EnsureVisualSystem(); if ((Object)(object)visualRoot == (Object)null) { RememberEvent("SKIP visual-root-unavailable target=" + GetTargetName(target)); return; } DamageCategory category = ResolveDamageCategory(hit); Vector3 impactPoint = ResolveImpactPoint(target, hit); Vector3 sprayDirection = ResolveOutwardSprayDirection(target, hit, impactPoint); Color bloodColor = ResolveBloodColor(target, category); float severity = Mathf.Clamp01((num - 0.35f) / 14f); bool lethalHit = health <= 0.01f; SpawnImpact(target, impactPoint, sprayDirection, num, severity, category, bloodColor, lethalHit); StartOrRefreshBleeding(target, impactPoint, num, severity, bloodColor); } private bool IsWithinVisualRange(Character target) { //IL_0032: 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_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } float num = Mathf.Clamp(visualDistance.Value, 5f, 150f); Vector3 val = ((Component)target).transform.position - ((Component)localPlayer).transform.position; return ((Vector3)(ref val)).sqrMagnitude <= num * num; } private bool CanSpawnLocalPlayerImpact() { if (allowPlayerImpactVisuals == null || !allowPlayerImpactVisuals.Value) { return false; } return !IsYoureInjuredLoaded(); } private static bool IsYoureInjuredLoaded() { try { return Chainloader.PluginInfos != null && Chainloader.PluginInfos.ContainsKey("com.marccunningham.youreinjured"); } catch { return false; } } private void EnsureVisualSystem() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)visualRoot != (Object)null)) { visualRoot = new GameObject("CrimsonBlood_LocalVisuals").transform; visualRoot.SetParent(((Component)this).transform, false); ((Object)((Component)visualRoot).gameObject).hideFlags = (HideFlags)61; PrepareTexturesAndMaterials(); PrewarmPools(); EnsureBandageAudioSource(); BeginLoadBandageAudioIfNeeded(); BeginLoadPlayerHitAudioIfNeeded(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Crimson Blood: " + assetLoadSummary)); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Crimson Blood: visual pools ready: bursts=" + burstPool.Count + ", streaks=" + streakPool.Count + ", droplets=" + dropletPool.Count + ", splats=" + splatPool.Count + ", bodyMarks=" + bodyMarkPool.Count + ".")); } } private void PrepareTexturesAndMaterials() { ClearLoadedAssetLists(); externalAssetsLoaded = false; activeAssetDirectory = string.Empty; bool flag = false; if (useExternalAssetTextures.Value) { string path = (string.IsNullOrWhiteSpace(assetRootFolder.Value) ? "assets" : assetRootFolder.Value.Trim()); activeAssetDirectory = Path.Combine(pluginDirectory ?? string.Empty, path); flag = TryLoadExternalTextureSet(activeAssetDirectory); } int count = burstTextures.Count; int count2 = trailTextures.Count; int count3 = splatTextures.Count; externalAssetsLoaded = count > 0 || count2 > 0 || count3 > 0; CreateFallbackTexturesIfNeeded(); if (burstTextures.Count == 0) { burstTextures.Add(fallbackBurstTexture); } if (dropletTextures.Count == 0) { dropletTextures.Add(fallbackDropletTexture); } if (splatTextures.Count == 0) { splatTextures.Add(fallbackSplatTexture); } quadMaterial = CreateTransparentMaterial("CrimsonBlood_AssetQuadMaterial", fallbackSplatTexture); ownedMaterials.Add(quadMaterial); if (trailTextures.Count == 0) { trailTextures.Add(fallbackTrailTexture); } string text = (flag ? "EXTERNAL" : (externalAssetsLoaded ? "PARTIAL" : "FALLBACK")); assetLoadSummary = "assetMode=" + text + " | root=" + (string.IsNullOrEmpty(activeAssetDirectory) ? "not-requested" : activeAssetDirectory) + " | externalBurst=" + count + " | externalTrail=" + count2 + " | externalSplat=" + count3 + " | activeBurst=" + burstTextures.Count + " | activeTrail=" + trailTextures.Count + " | activeSplat=" + splatTextures.Count; } private bool TryLoadExternalTextureSet(string assetRoot) { if (string.IsNullOrEmpty(assetRoot) || !Directory.Exists(assetRoot)) { assetLoadSummary = "assets folder missing: " + assetRoot; return false; } string folder = Path.Combine(assetRoot, "sprays", "blood_splash_frames"); string folder2 = Path.Combine(assetRoot, "trails"); string folder3 = (useHighDefinitionGroundSplats.Value ? Path.Combine(assetRoot, "ground_splats", "kenney_512") : Path.Combine(assetRoot, "ground_splats", "kenney_256")); int num = LoadPngFolder(folder, Mathf.Clamp(maximumBurstTextures.Value, 1, 16), burstTextures, "burst"); int num2 = LoadPngFolder(folder2, Mathf.Clamp(maximumTrailTextures.Value, 1, 16), trailTextures, "trail"); int num3 = LoadPngFolder(folder3, Mathf.Clamp(maximumGroundSplatTextures.Value, 1, 48), splatTextures, "splat"); for (int i = 0; i < trailTextures.Count; i++) { if ((Object)(object)trailTextures[i] != (Object)null) { dropletTextures.Add(trailTextures[i]); } } int num4; if (num > 0 && num2 > 0) { num4 = ((num3 > 0) ? 1 : 0); if (num4 != 0) { goto IL_014f; } } else { num4 = 0; } ((BaseUnityPlugin)this).Logger.LogWarning((object)"Crimson Blood: asset folders were found but one or more required texture groups were empty. Falling back only for missing groups."); goto IL_014f; IL_014f: return (byte)num4 != 0; } private int LoadPngFolder(string folder, int maximumTextures, List<Texture2D> destination, string groupName) { if (!Directory.Exists(folder)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Crimson Blood: missing " + groupName + " asset folder: " + folder)); return 0; } string[] files; try { files = Directory.GetFiles(folder, "*.png", SearchOption.TopDirectoryOnly); Array.Sort(files, (IComparer<string>?)StringComparer.OrdinalIgnoreCase); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Crimson Blood: could not enumerate " + groupName + " textures. " + ex.Message)); return 0; } int num = 0; for (int i = 0; i < files.Length; i++) { if (num >= maximumTextures) { break; } if (!CrimsonAssetLoader.TryLoadAlphaMaskPng(files[i], out var texture, out var error)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Crimson Blood: skipped " + groupName + " texture '" + Path.GetFileName(files[i]) + "'. " + error)); } else { ((Object)texture).name = "CrimsonBlood_" + groupName + "_" + Path.GetFileNameWithoutExtension(files[i]); destination.Add(texture); ownedTextures.Add(texture); num++; } } return num; } private void CreateFallbackTexturesIfNeeded() { if ((Object)(object)fallbackDropletTexture == (Object)null) { fallbackDropletTexture = CrimsonAssetLoader.CreateSoftCircleTexture("CrimsonBlood_FallbackDroplet", 64, 0.88f); ownedTextures.Add(fallbackDropletTexture); } if ((Object)(object)fallbackBurstTexture == (Object)null) { fallbackBurstTexture = CrimsonAssetLoader.CreateBurstTexture("CrimsonBlood_FallbackBurst", 128); ownedTextures.Add(fallbackBurstTexture); } if ((Object)(object)fallbackSplatTexture == (Object)null) { fallbackSplatTexture = CrimsonAssetLoader.CreateIrregularSplatTexture("CrimsonBlood_FallbackSplat", 128); ownedTextures.Add(fallbackSplatTexture); } if ((Object)(object)fallbackTrailTexture == (Object)null) { fallbackTrailTexture = CrimsonAssetLoader.CreateTrailTexture("CrimsonBlood_FallbackTrail", 32, 128); ownedTextures.Add(fallbackTrailTexture); } } private Material CreateTransparentMaterial(string materialName, Texture2D texture) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) Shader val = Shader.Find("Standard"); if ((Object)(object)val == (Object)null) { val = Shader.Find("Legacy Shaders/Transparent/Specular"); } if ((Object)(object)val == (Object)null) { val = Shader.Find("Particles/Alpha Blended"); } if ((Object)(object)val == (Object)null) { val = Shader.Find("Unlit/Transparent"); } if ((Object)(object)val == (Object)null) { val = Shader.Find("Sprites/Default"); } Material val2 = new Material(val); ((Object)val2).name = materialName; ((Object)val2).hideFlags = (HideFlags)61; val2.color = Color.white; val2.renderQueue = 3000; if ((Object)(object)texture != (Object)null) { if (val2.HasProperty("_MainTex")) { val2.SetTexture("_MainTex", (Texture)(object)texture); } if (val2.HasProperty("_BaseMap")) { val2.SetTexture("_BaseMap", (Texture)(object)texture); } } if (val2.HasProperty("_Mode")) { val2.SetFloat("_Mode", 3f); } if (val2.HasProperty("_Surface")) { val2.SetFloat("_Surface", 1f); } if (val2.HasProperty("_Blend")) { val2.SetFloat("_Blend", 0f); } if (val2.HasProperty("_SrcBlend")) { val2.SetInt("_SrcBlend", 5); } if (val2.HasProperty("_DstBlend")) { val2.SetInt("_DstBlend", 10); } if (val2.HasProperty("_ZWrite")) { val2.SetInt("_ZWrite", 0); } if (val2.HasProperty("_Cull")) { val2.SetInt("_Cull", 0); } if (val2.HasProperty("_Glossiness")) { val2.SetFloat("_Glossiness", 0.82f); } if (val2.HasProperty("_Smoothness")) { val2.SetFloat("_Smoothness", 0.82f); } if (val2.HasProperty("_Metallic")) { val2.SetFloat("_Metallic", 0f); } if (val2.HasProperty("_SpecColor")) { val2.SetColor("_SpecColor", new Color(0.22f, 0.08f, 0.08f, 1f)); } if (val2.HasProperty("_Color")) { val2.SetColor("_Color", Color.white); } if (val2.HasProperty("_BaseColor")) { val2.SetColor("_BaseColor", Color.white); } val2.SetOverrideTag("RenderType", "Transparent"); val2.DisableKeyword("_ALPHATEST_ON"); val2.EnableKeyword("_ALPHABLEND_ON"); val2.DisableKeyword("_ALPHAPREMULTIPLY_ON"); return val2; } private void PrewarmPools() { int num = Mathf.Clamp(burstPoolSize.Value, 4, 48); int num2 = Mathf.Clamp(streakPoolSize.Value, 4, 48); int num3 = Mathf.Clamp(dropletPoolSize.Value, 4, 240); int num4 = Mathf.Clamp(splatPoolSize.Value, 4, 240); int num5 = Mathf.Clamp(maxBodyMarksPerTarget.Value * 8, 4, 96); for (int i = 0; i < num; i++) { CreateBurstPoolObject(i); } for (int j = 0; j < num2; j++) { CreateStreakPoolObject(j); } for (int k = 0; k < num3; k++) { CreateDropletPoolObject(k); } for (int l = 0; l < num4; l++) { CreateSplatPoolObject(l); } for (int m = 0; m < num5; m++) { CreateBodyMarkPoolObject(m); } } private void CreateBurstPoolObject(int index) { GameObject obj = GameObject.CreatePrimitive((PrimitiveType)5); ((Object)obj).name = "CrimsonBlood_ImpactBurst_" + index; obj.transform.SetParent(visualRoot, false); ((Object)obj).hideFlags = (HideFlags)61; RemoveCollider(obj); Renderer component = obj.GetComponent<Renderer>(); ConfigureRenderer(component, quadMaterial); CrimsonBloodBurst crimsonBloodBurst = obj.AddComponent<CrimsonBloodBurst>(); crimsonBloodBurst.Initialize(this, component); obj.SetActive(false); burstPool.Add(crimsonBloodBurst); } private void CreateDropletPoolObject(int index) { GameObject obj = GameObject.CreatePrimitive((PrimitiveType)5); ((Object)obj).name = "CrimsonBlood_Droplet_" + index; obj.transform.SetParent(visualRoot, false); ((Object)obj).hideFlags = (HideFlags)61; RemoveCollider(obj); Renderer component = obj.GetComponent<Renderer>(); ConfigureRenderer(component, quadMaterial); CrimsonBloodDroplet crimsonBloodDroplet = obj.AddComponent<CrimsonBloodDroplet>(); crimsonBloodDroplet.Initialize(this, component); obj.SetActive(false); dropletPool.Add(crimsonBloodDroplet); } private void CreateStreakPoolObject(int index) { GameObject obj = GameObject.CreatePrimitive((PrimitiveType)5); ((Object)obj).name = "CrimsonBlood_Streak_" + index; obj.transform.SetParent(visualRoot, false); ((Object)obj).hideFlags = (HideFlags)61; RemoveCollider(obj); Renderer component = obj.GetComponent<Renderer>(); ConfigureRenderer(component, quadMaterial); CrimsonBloodStreak crimsonBloodStreak = obj.AddComponent<CrimsonBloodStreak>(); crimsonBloodStreak.Initialize(this, component); obj.SetActive(false); streakPool.Add(crimsonBloodStreak); } private void CreateSplatPoolObject(int index) { GameObject obj = GameObject.CreatePrimitive((PrimitiveType)5); ((Object)obj).name = "CrimsonBlood_Splat_" + index; obj.transform.SetParent(visualRoot, false); ((Object)obj).hideFlags = (HideFlags)61; RemoveCollider(obj); MeshRenderer component = obj.GetComponent<MeshRenderer>(); ConfigureRenderer((Renderer)(object)component, quadMaterial); CrimsonBloodSplat crimsonBloodSplat = obj.AddComponent<CrimsonBloodSplat>(); crimsonBloodSplat.Initialize(this, component); obj.SetActive(false); splatPool.Add(crimsonBloodSplat); } private void CreateBodyMarkPoolObject(int index) { GameObject obj = GameObject.CreatePrimitive((PrimitiveType)5); ((Object)obj).name = "CrimsonBlood_BodyMark_" + index; obj.transform.SetParent(visualRoot, false); ((Object)obj).hideFlags = (HideFlags)61; RemoveCollider(obj); MeshRenderer component = obj.GetComponent<MeshRenderer>(); ConfigureRenderer((Renderer)(object)component, quadMaterial); CrimsonBloodBodyMark crimsonBloodBodyMark = obj.AddComponent<CrimsonBloodBodyMark>(); crimsonBloodBodyMark.Initialize(this, component); obj.SetActive(false); bodyMarkPool.Add(crimsonBloodBodyMark); } private void EnsureBandageAudioSource() { if (!((Object)(object)bandageAudioSource != (Object)null)) { bandageAudioSource = ((Component)this).gameObject.GetComponent<AudioSource>(); if ((Object)(object)bandageAudioSource == (Object)null) { bandageAudioSource = ((Component)this).gameObject.AddComponent<AudioSource>(); } bandageAudioSource.playOnAwake = false; bandageAudioSource.loop = false; bandageAudioSource.spatialBlend = 0f; } } private static void RemoveCollider(GameObject gameObject) { Collider component = gameObject.GetComponent<Collider>(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } } private static void ConfigureRenderer(Renderer renderer, Material material) { if (!((Object)(object)renderer == (Object)null)) { renderer.sharedMaterial = material; renderer.shadowCastingMode = (ShadowCastingMode)0; renderer.receiveShadows = false; } } private void SpawnImpact(Character target, Vector3 impactPoint, Vector3 sprayDirection, float healthLoss, float severity, DamageCategory category, Color bloodColor, bool lethalHit) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_0385: Unknown result type (might be due to invalid IL or missing references) //IL_0386: Unknown result type (might be due to invalid IL or missing references) //IL_0389: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Unknown result type (might be due to invalid IL or missing references) //IL_03da: Unknown result type (might be due to invalid IL or missing references) //IL_03dc: Unknown result type (might be due to invalid IL or missing references) //IL_0403: Unknown result type (might be due to invalid IL or missing references) //IL_0404: Unknown result type (might be due to invalid IL or missing references) //IL_040b: Unknown result type (might be due to invalid IL or missing references) //IL_0419: Unknown result type (might be due to invalid IL or missing references) //IL_041a: Unknown result type (might be due to invalid IL or missing references) //IL_0421: Unknown result type (might be due to invalid IL or missing references) //IL_04d9: Unknown result type (might be due to invalid IL or missing references) //IL_03ed: Unknown result type (might be due to invalid IL or missing references) //IL_03ee: Unknown result type (might be due to invalid IL or missing references) //IL_03f5: Unknown result type (might be due to invalid IL or missing references) //IL_0359: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_0360: Unknown result type (might be due to invalid IL or missing references) //IL_0365: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Unknown result type (might be due to invalid IL or missing references) //IL_036e: Unknown result type (might be due to invalid IL or missing references) Vector3 val = impactPoint + sprayDirection * 0.035f + Vector3.up * 0.035f; bool flag = healthLoss >= 2.5f || severity >= 0.15f; bool flag2 = healthLoss >= 5.5f || severity >= 0.38f; bool flag3 = healthLoss >= 10f || severity >= 0.68f; if (category == DamageCategory.Fall) { SpawnBodyMarks(target, impactPoint, sprayDirection, severity, bloodColor, lethalHit); TryCreateImmediateImpactSplat(((Component)target).transform, val, Mathf.Lerp(0.2f, 0.7f, severity), bloodColor, Vector3.zero); visualEvents++; RememberEvent("SPAWN target=" + GetTargetName(target) + " loss=" + healthLoss.ToString("0.##") + " category=Fall splat-only"); return; } int num = Mathf.Clamp(maximumDropletsPerHit.Value, 2, 30); if (category == DamageCategory.Blunt && flag2) { num = Mathf.Clamp(bluntMaximumDropletsPerHit.Value, 4, 30); } int num2 = Mathf.Clamp(3 + Mathf.RoundToInt(Mathf.Lerp(2f, (float)num - 3f, severity)), 2, num); int num3 = (flag ? Mathf.Clamp(1 + Mathf.RoundToInt(Mathf.Lerp(0f, (float)maximumStreaksPerHeavyHit.Value - 1f, severity)), 1, Mathf.Max(1, maximumStreaksPerHeavyHit.Value)) : 0); int num4 = (flag ? 1 : 0); if (flag2) { num4 = Mathf.Clamp(maximumBurstsPerHeavyHit.Value, 1, 3); } switch (category) { case DamageCategory.Slash: case DamageCategory.Chop: num2 += 2; break; case DamageCategory.Pierce: num2++; num3 = Mathf.Max(num3, 1); break; case DamageCategory.Blunt: if (flag2) { float num5 = Mathf.Clamp(bluntBloodMultiplier.Value, 1f, 3f); num2 = Mathf.RoundToInt((float)num2 * num5) + 2; num3 = Mathf.Max(num3, 4); num4 = Mathf.Max(num4, 3); } else { num2++; } break; case DamageCategory.Fire: num2 = Mathf.Max(1, num2 - 6); num3 = 0; num4 = 0; break; case DamageCategory.Poison: num2 = Mathf.Max(1, num2 - 7); num3 = 0; num4 = 0; break; case DamageCategory.BiteOrMonsterHit: num2 += 2; break; } num2 = Mathf.Clamp(num2, 1, num); if (category != DamageCategory.Fire && category != DamageCategory.Poison && !(category == DamageCategory.Blunt && flag2)) { num2 = Mathf.Min(num + 2, num2 + 2); } num3 = Mathf.Clamp(num3, 0, Mathf.Clamp(maximumStreaksPerHeavyHit.Value, 0, 8)); num4 = Mathf.Clamp(num4, 0, Mathf.Clamp(maximumBurstsPerHeavyHit.Value + 1, 0, 4)); if (createImmediateImpactSplats.Value && healthLoss >= Mathf.Max(0f, minimumSplatHealthLoss.Value)) { float num6 = Mathf.Lerp(0.15f, flag3 ? 0.58f : 0.42f, severity); if (category == DamageCategory.Blunt && flag2) { num6 *= Mathf.Clamp(bluntSplatSizeMultiplier.Value, 1f, 2.5f); } TryCreateImmediateImpactSplat(((Component)target).transform, val + sprayDirection * 0.15f, num6, bloodColor, sprayDirection); } bool flag4 = lethalHit && UseGroundedLethalFinish; SpawnBodyMarks(target, impactPoint, sprayDirection, severity, bloodColor, lethalHit); if (flag4) { groundedLethalFinishes++; num4 = 0; num3 = 0; num2 = Mathf.Min(num2, 12); TryCreateImmediateImpactSplat(((Component)target).transform, impactPoint, Mathf.Clamp(0.34f + severity * 0.48f, 0.34f, 0.78f), bloodColor, sprayDirection); } if (!flag4) { SpawnAnimatedBursts(((Component)target).transform, val, sprayDirection, num4, severity, category, bloodColor); } SpawnPressureStreaks(((Component)target).transform, val, sprayDirection, num3, severity, category, bloodColor); SpawnBallisticDroplets(((Component)target).transform, val, sprayDirection, num2, severity, category, bloodColor, flag2, flag4); visualEvents++; string text = "SPAWN target=" + GetTargetName(target) + " loss=" + healthLoss.ToString("0.##") + " category=" + category.ToString() + " lethal=" + lethalHit + " burst=" + num4 + " streaks=" + num3 + " droplets=" + num2 + " direction=" + FormatVector(sprayDirection); RememberEvent(text); if (logVisualEvents.Value) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Crimson Blood " + text)); } } private void SpawnBodyMarks(Character target, Vector3 impactPoint, Vector3 sprayDirection, float severity, Color color, bool lethalHit) { //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_015b: 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_015c: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target == (Object)null || lethalHit || enableWoundBodyMarks == null || !enableWoundBodyMarks.Value) { return; } int num = Mathf.Clamp((maxBodyMarksPerTarget == null) ? 6 : maxBodyMarksPerTarget.Value, 1, 24); if (!bodyMarksByTarget.TryGetValue(target, out var value)) { value = new List<CrimsonBloodBodyMark>(); bodyMarksByTarget[target] = value; } CleanupBodyMarkList(value); while (value.Count >= num) { CrimsonBloodBodyMark crimsonBloodBodyMark = value[0]; for (int i = 1; i < value.Count; i++) { if ((Object)(object)value[i] != (Object)null && value[i].ActivatedAt < crimsonBloodBodyMark.ActivatedAt) { crimsonBloodBodyMark = value[i]; } } if ((Object)(object)crimsonBloodBodyMark != (Object)null) { crimsonBloodBodyMark.Deactivate(); } CleanupBodyMarkList(value); if (value.Count >= num && value.Count > 0) { value.RemoveAt(0); } } int num2 = ((!(severity >= 0.55f)) ? 1 : 2); for (int j = 0; j < num2; j++) { CrimsonBloodBodyMark bodyMark = GetBodyMark(); if ((Object)(object)bodyMark == (Object)null) { break; } Transform parent = FindBestBodyMarkParent(target, impactPoint); Vector3 val = impactPoint - target.GetCenterPoint(); if (((Vector3)(ref val)).sqrMagnitude < 0.0001f) { val = ((((Vector3)(ref sprayDirection)).sqrMagnitude > 0.0001f) ? sprayDirection : ((Component)target).transform.forward); } ((Vector3)(ref val)).Normalize(); Vector3 val2 = Vector3.Cross(Vector3.up, val); if (((Vector3)(ref val2)).sqrMagnitude < 0.0001f) { val2 = ((Component)target).transform.right; } ((Vector3)(ref val2)).Normalize(); Vector3 val3 = Vector3.Cross(val, val2); Vector3 normalized = ((Vector3)(ref val3)).normalized; Vector3 val4 = val2 * Random.Range(-0.045f, 0.045f) + normalized * Random.Range(-0.045f, 0.045f); float size = Mathf.Lerp(0.105f, 0.235f, Mathf.Clamp01(severity)) * Random.Range(0.82f, 1.24f); float lifetime = Mathf.Clamp((bodyMarkLifetimeSeconds == null) ? 180f : bodyMarkLifetimeSeconds.Value, 10f, 900f); Color val5 = color; val5.a = Mathf.Clamp(val5.a * 0.92f, 0.45f, 0.95f); bodyMark.Activate(parent, impactPoint + val4 + val * 0.018f, val, size, lifetime, val5, GetRandomSplatTexture(), Random.Range(0f, 360f)); value.Add(bodyMark); bodyMarksSpawned++; } } private static void CleanupBodyMarkList(List<CrimsonBloodBodyMark> marks) { for (int num = marks.Count - 1; num >= 0; num--) { CrimsonBloodBodyMark crimsonBloodBodyMark = marks[num]; if ((Object)(object)crimsonBloodBodyMark == (Object)null || !((Component)crimsonBloodBodyMark).gameObject.activeSelf) { marks.RemoveAt(num); } } } private Transform FindBestBodyMarkParent(Character target, Vector3 impactPoint) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target == (Object)null) { return visualRoot; } Transform result = ((Component)target).transform; float num = float.MaxValue; GameObject visual = target.GetVisual(); Transform[] componentsInChildren = ((Component)(((Object)(object)visual != (Object)null) ? visual.transform : ((Component)target).transform)).GetComponentsInChildren<Transform>(true); foreach (Transform val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { Vector3 val2 = val.position - impactPoint; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; result = val; } } } return result; } private void StartOrRefreshBleeding(Character target, Vector3 impactPoint, float healthLoss, float severity, Color bloodColor) { //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target == (Object)null || enableBleedingTrails == null || !enableBleedingTrails.Value) { return; } float num = ((trailMinimumHealthLoss == null) ? 2f : Mathf.Max(0f, trailMinimumHealthLoss.Value)); if (healthLoss < num) { return; } bool flag = (Object)(object)target == (Object)(object)Player.m_localPlayer; if (!bleedingActors.TryGetValue(target, out var value)) { int num2 = ((maxBleedingActors == null) ? 32 : Mathf.Clamp(maxBleedingActors.Value, 1, 128)); if (bleedingActors.Count >= num2) { RememberEvent("SKIP bleed-cap target=" + GetTargetName(target)); return; } value = new BleedState { Target = target, LastDripPosition = impactPoint, LastDripTime = Time.time, IsPlayer = flag, Intensity = Mathf.Clamp(0.55f + severity * 1.15f, 0.55f, 1.7f), Color = bloodColor }; bleedingActors[target] = value; bleedingActorsStarted++; RememberEvent("BLEED start target=" + GetTargetName(target) + " player=" + flag); if (flag) { float num3 = ((playerBleedTickSeconds == null) ? 2f : Mathf.Clamp(playerBleedTickSeconds.Value, 0.25f, 30f)); nextPlayerBleedDamageTime = Time.time + num3; } } else { value.Intensity = Mathf.Max(value.Intensity, Mathf.Clamp(0.55f + severity * 1.15f, 0.55f, 1.7f)); value.Color = bloodColor; value.IsPlayer |= flag; } } private void UpdatePlayerBleedDamage() { //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Expected O, but got Unknown //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) if (enablePlayerBleedDamage == null || !enablePlayerBleedDamage.Value) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || !bleedingActors.ContainsKey((Character)(object)localPlayer) || IsPlayerDamageProtected(localPlayer)) { return; } float num = ((playerBleedTickSeconds == null) ? 2f : Mathf.Clamp(playerBleedTickSeconds.Value, 0.25f, 30f)); if (Time.time < nextPlayerBleedDamageTime) { return; } nextPlayerBleedDamageTime = Time.time + num; float num2 = ((playerBleedDamagePerTick == null) ? 0.5f : Mathf.Max(0f, playerBleedDamagePerTick.Value)); if (num2 <= 0f) { return; } bool num3 = playerBleedCanKill == null || playerBleedCanKill.Value; float health = ((Character)localPlayer).GetHealth(); if (!num3) { float num4 = Mathf.Max(0f, health - 1f); num2 = Mathf.Min(num2, num4); if (num2 <= 0f) { return; } } HitData val = new HitData(); val.m_damage.m_damage = num2; val.m_point = ((Component)localPlayer).transform.position; val.m_dir = Vector3.up; val.m_hitType = (HitType)0; ((Character)localPlayer).Damage(val); playerBleedDamageTicks++; } private bool IsPlayerDamageProtected(Player player) { try { if (((Character)player).InGodMode()) { return true; } } catch { } return false; } private void UpdateBleedingTrails() { //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) if (enableBleedingTrails == null || !enableBleedingTrails.Value || bleedingActors.Count == 0 || Time.time < nextBleedUpdateTime) { return; } nextBleedUpdateTime = Time.time + 0.12f; bleedingActorsToRemove.Clear(); foreach (KeyValuePair<Character, BleedState> bleedingActor in bleedingActors) { Character key = bleedingActor.Key; BleedState value = bleedingActor.Value; if ((Object)(object)key == (Object)null || (Object)(object)((Component)key).gameObject == (Object)null || key.IsDead()) { bleedingActorsToRemove.Add(key); continue; } if (!value.IsPlayer && (Object)(object)key == (Object)(object)Player.m_localPlayer) { value.IsPlayer = true; } if (ShouldEmitTrailFor(key)) { Vector3 position = ((Component)key).transform.position; float num = ((trailDripDistance == null) ? 1.2f : Mathf.Clamp(trailDripDistance.Value, 0.35f, 4f)); float num2 = ((trailDripInterval == null) ? 0.6f : Mathf.Clamp(trailDripInterval.Value, 0.15f, 5f)); Vector3 val = position - value.LastDripPosition; bool num3 = ((Vector3)(ref val)).sqrMagnitude >= num * num; bool flag = Time.time - value.LastDripTime >= num2; if ((num3 || flag) && EmitBleedTrailDrip(key, value)) { value.LastDripPosition = position; value.LastDripTime = Time.time; } } } for (int i = 0; i < bleedingActorsToRemove.Count; i++) { Character val2 = bleedingActorsToRemove[i]; if ((Object)(object)val2 != (Object)null) { bleedingActors.Remove(val2); } else { PruneNullBleedKeys(); } bleedingActorsPruned++; } } private bool ShouldEmitTrailFor(Character target) { //IL_0027: 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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)target == (Object)null) { return false; } float num = SurfaceSplatRenderDistance; Vector3 position = ((Component)localPlayer).transform.position; Vector3 val = ((Component)target).transform.position - position; return ((Vector3)(ref val)).sqrMagnitude <= num * num; } private bool EmitBleedTrailDrip(Character target, BleedState state) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012c: 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) if ((Object)(object)target == (Object)null || (Object)(object)visualRoot == (Object)null) { return false; } Vector3 origin = ((Component)target).transform.position + Vector3.up * 1.4f; if (!TryFindSurface(((Component)target).transform, origin, Vector3.down, 4.6f, out var bestHit)) { origin = target.GetCenterPoint() + Vector3.up * 0.55f; if (!TryFindSurface(((Component)target).transform, origin, Vector3.down, 6.4f, out bestHit)) { return false; } } float num = ((trailDripSizeMultiplier == null) ? 0.55f : Mathf.Clamp(trailDripSizeMultiplier.Value, 0.15f, 1.25f)); float size = Mathf.Clamp(Random.Range(0.13f, 0.32f) * num * Mathf.Clamp(state.Intensity, 0.45f, 1.8f), 0.045f, 0.34f); Color color = state.Color; color.a = Mathf.Clamp(color.a * Random.Range(0.62f, 0.9f), 0.34f, 0.92f); CreateSurfaceSplat(((RaycastHit)(ref bestHit)).point, ((RaycastHit)(ref bestHit)).normal, size, color, null); trailDripsSpawned++; return true; } private void PruneNullBleedKeys() { bleedingActorsToRemove.Clear(); foreach (KeyValuePair<Character, BleedState> bleedingActor in bleedingActors) { if ((Object)(object)bleedingActor.Key == (Object)null) { bleedingActorsToRemove.Add(bleedingActor.Key); } } for (int i = 0; i < bleedingActorsToRemove.Count; i++) { bleedingActors.Remove(bleedingActorsToRemove[i]); } } private void HandleBandageInput() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (bandageKey == null || !Input.GetKeyDown(bandageKey.Value)) { return; } Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { if (!bleedingActors.ContainsKey((Character)(object)localPlayer)) { RememberEvent("BANDAGE none"); } else if (requireBandageMaterial != null && requireBandageMaterial.Value && !TryConsumeBandageMaterial(localPlayer)) { bandageAttemptsFailed++; TryShowPlayerMessage(localPlayer, "Need " + GetBandageCost() + "x " + GetBandageItemName() + " to bandage."); } else { bleedingActors.Remove((Character)(object)localPlayer); bandagesApplied++; TryShowPlayerMessage(localPlayer, "Bleeding treated."); PlayBandageAudio(); RememberEvent("BANDAGE applied"); } } } private bool TryConsumeBandageMaterial(Player player) { if ((Object)(object)player == (Object)null) { return false; } int bandageCost = GetBandageCost(); if (bandageCost <= 0) { return true; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return false; } string bandageItemName = GetBandageItemName(); if (inventory.CountItems(bandageItemName, -1, true) < bandageCost) { return false; } inventory.RemoveItem(bandageItemName, bandageCost, -1, true); TryInvokeNoArg(player, "OnInventoryChanged"); return true; } private int GetBandageCost() { if (bandageItemCost != null) { return Mathf.Clamp(bandageItemCost.Value, 0, 20); } return 1; } private string GetBandageItemName() { string text = ((bandageItemSharedName == null) ? "$item_leatherscraps" : bandageItemSharedName.Value); if (!string.IsNullOrWhiteSpace(text)) { return text.Trim(); } return "$item_leatherscraps"; } private static void TryShowPlayerMessage(Player player, string message) { if ((Object)(object)player == (Object)null || string.IsNullOrEmpty(message)) { return; } try { ((Character)player).Message((MessageType)2, message, 0, (Sprite)null); } catch { } } private void BeginLoadBandageAudioIfNeeded() { if (!bandageAudioLoadAttempted && !bandageAudioLoadInProgress && enableBandageAudio != null && enableBandageAudio.Value) { string text = ResolveBandageAudioPath(); if (string.IsNullOrEmpty(text) || !File.Exists(text)) { bandageAudioLoadAttempted = true; ((BaseUnityPlugin)this).Logger.LogWarning((object)("Crimson Blood: bandage audio not found at " + text)); } else { bandageAudioLoadInProgress = true; loadedBandageAudioPath = text; ((MonoBehaviour)this).StartCoroutine(LoadBandageAudioCoroutine(text)); } } } private string ResolveBandageAudioPath() { return ResolveAudioPath((bandageAudioFile == null) ? null : bandageAudioFile.Value, "bandage.mp3"); } private string ResolveAudioPath(string configuredFileName, string defaultFileName) { string text = configuredFileName; if (string.IsNullOrWhiteSpace(text)) { text = defaultFileName; } string path = ((assetRootFolder == null || string.IsNullOrWhiteSpace(assetRootFolder.Value)) ? "assets" : assetRootFolder.Value.Trim()); return Path.Combine(pluginDirectory ?? string.Empty, path, "audio", text.Trim()); } private IEnumerator LoadBandageAudioCoroutine(string path) { bandageAudioLoadAttempted = true; yield return LoadAudioClipCoroutine(path, "CrimsonBlood_Bandage", delegate(AudioClip clip) { bandageAudioClip = clip; }); bandageAudioLoadInProgress = false; } private void BeginLoadPlayerHitAudioIfNeeded() { if (!playerHitAudioLoadAttempted && !playerHitAudioLoadInProgress && enablePlayerHitSounds != null && enablePlayerHitSounds.Value) { playerHitAudioLoadInProgress = true; ((MonoBehaviour)this).StartCoroutine(LoadPlayerHitAudioCoroutine()); } } private IEnumerator LoadPlayerHitAudioCoroutine() { playerHitAudioLoadAttempted = true; yield return LoadAudioClipCoroutine(ResolveAudioPath((swordHitSoundFile == null) ? null : swordHitSoundFile.Value, "sword_hit.mp3"), "CrimsonBlood_SwordHit", delegate(AudioClip clip) { swordHitAudioClip = clip; }); yield return LoadAudioClipCoroutine(ResolveAudioPath((axeHitSoundFile == null) ? null : axeHitSoundFile.Value, "axe_hit.mp3"), "CrimsonBlood_AxeHit", delegate(AudioClip clip) { axeHitAudioClip = clip; }); yield return LoadAudioClipCoroutine(ResolveAudioPath((arrowHitSoundFile == null) ? null : arrowHitSoundFile.Value, "arrow_hit.mp3"), "CrimsonBlood_ArrowHit", delegate(AudioClip clip) { arrowHitAudioClip = clip; }); playerHitAudioLoadInProgress = false; } private IEnumerator LoadAudioClipCoroutine(string path, string clipName, Action<AudioClip> assignClip) { if (string.IsNullOrEmpty(path) || !File.Exists(path)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Crimson Blood: audio file not found at " + path)); yield break; } string absoluteUri = new Uri(path).AbsoluteUri; UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(absoluteUri, (AudioType)13); try { yield return request.SendWebRequest(); if ((int)request.result != 2 && (int)request.result != 3 && (int)request.result != 4) { AudioClip content = DownloadHandlerAudioClip.GetContent(request); if ((Object)(object)content != (Object)null) { ((Object)content).name = clipName; assignClip?.Invoke(content); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Crimson Blood: loaded audio " + path)); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Crimson Blood: audio request succeeded but returned no clip: " + path)); } } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Crimson Blood: failed to load audio " + path + ": " + request.error)); } } finally { ((IDisposable)request)?.Dispose(); } } private void PlayBandageAudio() { if (enableBandageAudio != null && enableBandageAudio.Value) { EnsureBandageAudioSource(); BeginLoadBandageAudioIfNeeded(); if (!((Object)(object)bandageAudioSource == (Object)null) && !((Object)(object)bandageAudioClip == (Object)null)) { bandageAudioSource.PlayOneShot(bandageAudioClip, Mathf.Clamp01((bandageAudioVolume == null) ? 0.9f : bandageAudioVolume.Value)); } } } private void TryPlayPlayerHitSound(HitData hit) { if (enablePlayerHitSounds == null || !enablePlayerHitSounds.Value) { return; } PlayerHitSoundKind playerHitSoundKind = ResolvePlayerHitSoundKind(hit); if (playerHitSoundKind == PlayerHitSoundKind.None) { return; } if (Time.unscaledTime < nextPlayerHitSoundTime) { playerHitSoundsSkipped++; return; } EnsureBandageAudioSource(); BeginLoadPlayerHitAudioIfNeeded(); AudioClip playerHitAudioClip = GetPlayerHitAudioClip(playerHitSoundKind); if ((Object)(object)bandageAudioSource == (Object)null || (Object)(object)playerHitAudioClip == (Object)null) { playerHitSoundsSkipped++; return; } nextPlayerHitSoundTime = Time.unscaledTime + 0.16f; bandageAudioSource.PlayOneShot(playerHitAudioClip, Mathf.Clamp01((playerHitSoundVolume == null) ? 0.9f : playerHitSoundVolume.Value)); playerHitSoundsPlayed++; } private AudioClip GetPlayerHitAudioClip(PlayerHitSoundKind kind) { return (AudioClip)(kind switch { PlayerHitSoundKind.Sword => swordHitAudioClip, PlayerHitSoundKind.Axe => axeHitAudioClip, PlayerHitSoundKind.Arrow => arrowHitAudioClip, _ => null, }); } private PlayerHitSoundKind ResolvePlayerHitSoundKind(HitData hit) { if (hit == null) { return PlayerHitSoundKind.None; } string value = TryGetHitSkillName(hit); if (NameContains(value, "sword")) { return PlayerHitSoundKind.Sword; } if (NameContains(value, "axe")) { return PlayerHitSoundKind.Axe; } if (NameContains(value, "bow") || NameContains(value, "crossbow")) { return PlayerHitSoundKind.Arrow; } string value2 = ResolveHitSourceName(hit); if (NameContains(value2, "sword")) { return PlayerHitSoundKind.Sword; } if (NameContains(value2, "axe")) { return PlayerHitSoundKind.Axe; } if (NameContains(value2, "arrow") || NameContains(value2, "bow") || NameContains(value2, "bolt")) { return PlayerHitSoundKind.Arrow; } return PlayerHitSoundKind.None; } private static string TryGetHitSkillName(HitData hit) { if (hit == null) { return string.Empty; } try { FieldInfo field = typeof(HitData).GetField("m_skill", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); object obj = ((field != null) ? field.GetValue(hit) : null); return (obj == null) ? string.Empty : obj.ToString(); } catch { return string.Empty; } } private static string ResolveHitSourceName(HitData hit) { string sourceName = string.Empty; if (hit == null) { return sourceName; } try { FieldInfo[] fields = typeof(HitData).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { string text = fieldInfo.Name.ToLowerInvariant(); if (text.Contains("item") || text.Contains("weapon") || text.Contains("ammo") || text.Contains("projectile")) { AppendPossibleSourceName(fieldInfo.GetValue(hit), ref sourceName); } } } catch { } Character val = TryGetHitAttacker(hit); if ((Object)(object)val != (Object)null) { AppendPossibleSourceName(TryInvokeNoArg(val, "GetCurrentWeapon"), ref sourceName); AppendPossibleSourceName(TryInvokeNoArg(val, "GetAmmoItem"), ref sourceName); } return sourceName; } private static Character TryGetHitAttacker(HitData hit) { if (hit == null) { return null; } try { MethodInfo method = typeof(HitData).GetMethod("GetAttacker", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); return (Character)((method == null) ? null : /*isinst with value type is only supported in some contexts*/); } catch { return null; } } private static object TryInvokeNoArg(object instance, string methodName) { if (instance == null || string.IsNullOrEmpty(methodName)) { return null; } try { MethodInfo method = instance.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); return (method == null) ? null : method.Invoke(instance, null); } catch { return null; } } private static void AppendPossibleSourceName(object value, ref string sourceName) { if (value == null) { return; } string value2 = value as string; if (!string.IsNullOrWhiteSpace(value2)) { AppendSourceName(value2, ref sourceName); return; } Object val = (Object)((value is Object) ? value : null); if (val != (Object)null) { AppendSourceName(val.name, ref sourceName); Component val2 = (Component)(object)((val is Component) ? val : null); if ((Object)(object)val2 != (Object)null && (Object)(object)val2.gameObject != (Object)null) { AppendSourceName(((Object)val2.gameObject).name, ref sourceName); } } Type type = value.GetType(); AppendStringField(value, type, "m_name", ref sourceName); AppendStringField(value, type, "name", ref sourceName); try { FieldInfo field = type.GetField("m_shared", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); object obj = ((field != null) ? field.GetValue(value) : null); if (obj != null) { Type type2 = obj.GetType(); AppendStringField(obj, type2, "m_name", ref sourceName); AppendStringField(obj, type2, "name", ref sourceName); } } catch { } } private static void AppendStringField(object instance, Type type, string fieldName, ref string sourceName) { if (instance == null || type == null || string.IsNullOrEmpty(fieldName)) { return; } try { FieldInfo field = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); AppendSourceName((field != null) ? (field.GetValue(instance) as string) : null, ref sourceName); } catch { } } private static void AppendSourceName(string value, ref string sourceName) { if (!string.IsNullOrWhiteSpace(value)) { if (sourceName.Length > 0) { sourceName += " "; } sourceName += value; } } private static bool NameContains(string value, string token) { if (!string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(token)) { return value.IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0; } return false; } private void SpawnAnimatedBursts(Transform owner, Vector3 source, Vector3 sprayDirection, int burstCount, float severity, DamageCategory category, Color color) { //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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) if (burstCount <= 0 || burstPool.Count == 0) { return; } Vector3 val = Vector3.Cross(Vector3.up, sprayDirection); if (((Vector3)(ref val)).sqrMagnitude < 0.0001f) { val = Vector3.right; } ((Vector3)(ref val)).Normalize(); float num = Mathf.Lerp(0.52f, 1.2f, severity); for (int i = 0; i < burstCount; i++) { float num2 = Random.Range(-0.2f, 0.2f); float num3 = Random.Range(-0.06f, 0.16f); Vector3 val2 = sprayDirection + val * num2 + Vector3.up * num3; Vector3 normalized = ((Vector3)(ref val2)).normalized; float num4 = num * Random.Range(0.85f, 1.18f); float num5 = Mathf.Max(0.06f, burstLifetime.Value) * Random.Range(0.9f, 1.12f); Vector3 val3 = normalized * Random.Range(0.45f, 1.35f) + Vector3.up * Random.Range(0.05f, 0.45f); switch (category) { case DamageCategory.Pierce: num4 *= 0.82f; val3 *= 1.3f; break; case DamageCategory.Blunt: { float num6 = Mathf.Clamp(bluntBurstSizeMultiplier.Value, 1f, 2.5f); num4 *= num6; num5 *= Mathf.Clamp(bluntLifetimeMultiplier.Value, 1f, 2.5f); val3 *= 1.1f; break; } } GetBurst().Activate(owner, source + normalized * 0.02f, val3, num4, num5, color, burstTextures, Random.Range(0f, 360f)); burstsSpawned++; } } private void SpawnPressureStreaks(Transform owner, Vector3 source, Vector3 sprayDirection, int streakCount, float severity, DamageCategory category, Color color) { //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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) if (streakCount <= 0 || streakPool.Count == 0) { return; } Vector3 val = Vector3.Cross(Vector3.up, sprayDirection); if (((Vector3)(ref val)).sqrMagnitude < 0.0001f) { val = Vector3.right; } ((Vector3)(ref val)).Normalize(); float num = Mathf.Lerp(5.5f, 12f, severity); float num2 = Mathf.Lerp(0.75f, 2.25f, severity); for (int i = 0; i < streakCount; i++) { float num3 = ((category == DamageCategory.Blunt) ? 0.78f : 0.44f); float num4 = Random.Range(0f - num3, num3); float num5 = Random.Range(-0.06f, 0.3f); Vector3 val2 = sprayDirection + val * num4 + Vector3.up * num5; Vector3 normalized = ((Vector3)(ref val2)).normalized; float num6 = num * Random.Range(0.84f, 1.24f); float num7 = Mathf.Lerp(0.028f, 0.078f, severity) * Random.Range(0.85f, 1.18f); float num8 = Mathf.Max(0.06f, streakLifetime.Value) * Random.Range(0.88f, 1.15f); Vector3 val3 = normalized * num6 + Vector3.up * num2; switch (category) { case DamageCategory.Pierce: val3 *= 1.16f; num7 *= 0.84f; break; case DamageCategory.Blunt: val3 *= 1.05f; num7 *= 1.65f; num8 *= Mathf.Clamp(bluntLifetimeMultiplier.Value, 1f, 2.5f); break; case DamageCategory.BiteOrMonsterHit: val3 += val * Random.Range(-1f, 1f); break; } GetStreak().Activate(owner, source, val3, num7, num8, color, GetRandomTrailTexture(), Random.Range(0f, 360f)); streaksSpawned++; } } private void SpawnBallisticDroplets(Transform owner, Ve