Decompiled source of HealthPulse v0.1.0

HealthPulse.dll

Decompiled 3 days ago
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace HealthPulse;

[BepInPlugin("j1ga.healthpulse", "HealthPulse", "0.1.0")]
public class HealthPulsePlugin : BaseUnityPlugin
{
	public const string Guid = "j1ga.healthpulse";

	public const string Name = "HealthPulse";

	public const string Version = "0.1.0";

	private const string SecGeneral = "01 General";

	private const string SecLook = "02 Look";

	private const string SecHit = "03 Hit flash";

	private const string SecSound = "04 Heartbeat sound";

	private const int ConfigVersion = 2;

	private ConfigEntry<bool> _enabled;

	private ConfigEntry<float> _threshold;

	private ConfigEntry<bool> _hideWithHud;

	private ConfigEntry<float> _maxAlpha;

	private ConfigEntry<Color> _color;

	private ConfigEntry<float> _width;

	private ConfigEntry<float> _bpmMin;

	private ConfigEntry<float> _bpmMax;

	private ConfigEntry<bool> _hitFlash;

	private ConfigEntry<float> _hitStrength;

	private ConfigEntry<bool> _sound;

	private ConfigEntry<float> _soundThreshold;

	private ConfigEntry<float> _soundVolume;

	private ConfigEntry<int> _configVersion;

	private Image _image;

	private GameObject _root;

	private Texture2D _tex;

	private Sprite _sprite;

	private float _texWidth = -1f;

	private Hud _hud;

	private AudioSource _audio;

	private AudioClip _beat;

	private bool _soundLogged;

	private float _phase;

	private float _shown;

	private float _flash;

	private float _lastHealth = -1f;

	private void Awake()
	{
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Expected O, but got Unknown
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Expected O, but got Unknown
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0129: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Expected O, but got Unknown
		//IL_0168: Unknown result type (might be due to invalid IL or missing references)
		//IL_0172: Expected O, but got Unknown
		//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b1: Expected O, but got Unknown
		//IL_0207: Unknown result type (might be due to invalid IL or missing references)
		//IL_0211: Expected O, but got Unknown
		//IL_0267: Unknown result type (might be due to invalid IL or missing references)
		//IL_0271: Expected O, but got Unknown
		//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b0: Expected O, but got Unknown
		_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("01 General", "Enabled", true, "Show the low-health vignette.");
		_threshold = ((BaseUnityPlugin)this).Config.Bind<float>("01 General", "Threshold", 35f, new ConfigDescription("Health, in percent of the maximum, below which the edges start to glow. The lower the health, the stronger and faster.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 90f), new object[0]));
		_hideWithHud = ((BaseUnityPlugin)this).Config.Bind<bool>("01 General", "HideWithHud", false, "Hide the vignette too when the HUD is hidden (Ctrl+F3). Off: it stays, as a warning.");
		_maxAlpha = ((BaseUnityPlugin)this).Config.Bind<float>("02 Look", "MaxStrength", 0.75f, new ConfigDescription("How opaque the edges get at the lowest health (0..1).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), new object[0]));
		_color = ((BaseUnityPlugin)this).Config.Bind<Color>("02 Look", "Color", new Color(0.75f, 0f, 0f, 1f), "Colour of the vignette.");
		_width = ((BaseUnityPlugin)this).Config.Bind<float>("02 Look", "Width", 0.45f, new ConfigDescription("How far the glow reaches in from the edges (0.2 a thin frame .. 0.8 most of the screen).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 0.8f), new object[0]));
		_bpmMin = ((BaseUnityPlugin)this).Config.Bind<float>("02 Look", "BeatsPerMinuteAtThreshold", 45f, new ConfigDescription("Heart rate of the pulse just below the threshold.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 200f), new object[0]));
		_bpmMax = ((BaseUnityPlugin)this).Config.Bind<float>("02 Look", "BeatsPerMinuteAtZero", 80f, new ConfigDescription("Heart rate of the pulse near zero health.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 220f), new object[0]));
		_hitFlash = ((BaseUnityPlugin)this).Config.Bind<bool>("03 Hit flash", "Enabled", true, "Flash the edges when you take a hit; the lower the health, the stronger.");
		_hitStrength = ((BaseUnityPlugin)this).Config.Bind<float>("03 Hit flash", "Strength", 1f, new ConfigDescription("How strong the flash is.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), new object[0]));
		_sound = ((BaseUnityPlugin)this).Config.Bind<bool>("04 Heartbeat sound", "Enabled", false, "Play a heartbeat with the pulse when health is very low.");
		_soundThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("04 Heartbeat sound", "Threshold", 15f, new ConfigDescription("Health, in percent, below which the heartbeat is heard.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 90f), new object[0]));
		_soundVolume = ((BaseUnityPlugin)this).Config.Bind<float>("04 Heartbeat sound", "Volume", 0.8f, new ConfigDescription("Volume of the heartbeat (the game's sound volume applies on top).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[0]));
		_configVersion = ((BaseUnityPlugin)this).Config.Bind<int>("99 Internal", "ConfigVersion", 0, "Used by the mod to update old defaults once. Do not change.");
		MigrateConfig();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"HealthPulse 0.1.0 loaded. No Harmony patches are applied.");
	}

	private void MigrateConfig()
	{
		if (_configVersion.Value < 2)
		{
			if (Mathf.Approximately(_bpmMin.Value, 60f))
			{
				_bpmMin.Value = 45f;
			}
			if (Mathf.Approximately(_bpmMax.Value, 120f))
			{
				_bpmMax.Value = 80f;
			}
			if (Mathf.Approximately(_soundVolume.Value, 0.6f))
			{
				_soundVolume.Value = 0.8f;
			}
			_configVersion.Value = 2;
		}
	}

	private void OnDestroy()
	{
		if ((Object)(object)_root != (Object)null)
		{
			Object.Destroy((Object)(object)_root);
		}
		if ((Object)(object)_sprite != (Object)null)
		{
			Object.Destroy((Object)(object)_sprite);
		}
		if ((Object)(object)_tex != (Object)null)
		{
			Object.Destroy((Object)(object)_tex);
		}
		if ((Object)(object)_beat != (Object)null)
		{
			Object.Destroy((Object)(object)_beat);
		}
	}

	private void Update()
	{
		try
		{
			Tick();
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Update failed, the vignette is off for the session: " + ex));
			((Behaviour)this).enabled = false;
			if ((Object)(object)_image != (Object)null)
			{
				((Behaviour)_image).enabled = false;
			}
		}
	}

	private void Tick()
	{
		//IL_0351: Unknown result type (might be due to invalid IL or missing references)
		//IL_0356: Unknown result type (might be due to invalid IL or missing references)
		//IL_0367: Unknown result type (might be due to invalid IL or missing references)
		Hud instance = Hud.instance;
		if ((Object)(object)instance == (Object)null)
		{
			_hud = null;
			_image = null;
			return;
		}
		if ((Object)(object)instance != (Object)(object)_hud || (Object)(object)_image == (Object)null)
		{
			Attach(instance);
		}
		if ((Object)(object)_image == (Object)null)
		{
			return;
		}
		if (!Mathf.Approximately(_texWidth, _width.Value))
		{
			MakeTexture();
		}
		float unscaledDeltaTime = Time.unscaledDeltaTime;
		Player localPlayer = Player.m_localPlayer;
		bool flag = _enabled.Value && (Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead() && !((Character)localPlayer).IsTeleporting() && !localPlayer.IsSleeping() && (!_hideWithHud.Value || !instance.m_userHidden);
		float num = (((Object)(object)localPlayer != (Object)null) ? ((Character)localPlayer).GetMaxHealth() : 0f);
		float num2 = (((Object)(object)localPlayer != (Object)null && num > 0f) ? Mathf.Clamp01(((Character)localPlayer).GetHealth() / num) : 1f);
		float num3 = _threshold.Value / 100f;
		float num4 = (flag ? Mathf.Clamp01((num3 - num2) / num3) : 0f);
		bool flag2 = flag && num2 < num3;
		float num5 = Mathf.Lerp(_bpmMin.Value, _bpmMax.Value, num4);
		float phase = _phase;
		_phase += unscaledDeltaTime * num5 / 60f;
		if (_phase >= 1f)
		{
			_phase -= Mathf.Floor(_phase);
		}
		bool flag3 = _phase >= 0.02f && (phase < 0.02f || phase > _phase);
		float num6 = Mathf.Max(Bump(_phase, 0.08f), 0.55f * Bump(_phase, 0.38f));
		if (flag && _lastHealth >= 0f && (Object)(object)localPlayer != (Object)null)
		{
			float num7 = (_lastHealth - ((Character)localPlayer).GetHealth()) / Mathf.Max(1f, num);
			if (_hitFlash.Value && num7 > 0.005f)
			{
				float num8 = Mathf.Clamp01((num3 * 1.5f - num2) / (num3 * 1.5f));
				_flash = Mathf.Min(1f, _flash + (0.35f + num7 * 3f) * num8 * _hitStrength.Value);
			}
		}
		_lastHealth = ((flag && (Object)(object)localPlayer != (Object)null) ? ((Character)localPlayer).GetHealth() : (-1f));
		_flash = Mathf.MoveTowards(_flash, 0f, unscaledDeltaTime / 0.45f);
		float num9 = (flag2 ? ((0.3f + 0.7f * num4) * (0.72f + 0.28f * num6)) : 0f);
		_shown = Mathf.Lerp(_shown, num9, 1f - Mathf.Exp((0f - unscaledDeltaTime) / 0.12f));
		float num10 = Mathf.Clamp01(Mathf.Max(_shown, _flash) * _maxAlpha.Value);
		Color value = _color.Value;
		value.a = num10;
		((Graphic)_image).color = value;
		((Behaviour)_image).enabled = num10 > 0.003f;
		if (flag3 && _sound.Value && flag && num2 < _soundThreshold.Value / 100f)
		{
			PlayBeat(num2);
		}
	}

	private static float Bump(float x, float c)
	{
		float num = x - c;
		if (num > 0.5f)
		{
			num -= 1f;
		}
		else if (num < -0.5f)
		{
			num += 1f;
		}
		num /= 0.15f;
		return Mathf.Exp((0f - num) * num);
	}

	private void Attach(Hud hud)
	{
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Expected O, but got Unknown
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Expected O, but got Unknown
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		_hud = hud;
		_image = null;
		if ((Object)(object)_root != (Object)null)
		{
			Object.Destroy((Object)(object)_root);
		}
		Canvas val = (((Object)(object)hud.m_rootObject != (Object)null) ? hud.m_rootObject.GetComponentInParent<Canvas>() : null);
		Canvas val2 = (((Object)(object)val != (Object)null) ? val.rootCanvas : null);
		if ((Object)(object)val2 == (Object)null)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"The HUD's canvas was not found; no vignette.");
			return;
		}
		_root = new GameObject("HealthPulse", new Type[1] { typeof(RectTransform) });
		_root.layer = ((Component)val2).gameObject.layer;
		RectTransform val3 = (RectTransform)_root.transform;
		((Transform)val3).SetParent(((Component)val2).transform, false);
		((Transform)val3).SetAsFirstSibling();
		val3.anchorMin = Vector2.zero;
		val3.anchorMax = Vector2.one;
		val3.offsetMin = Vector2.zero;
		val3.offsetMax = Vector2.zero;
		Canvas val4 = _root.AddComponent<Canvas>();
		val4.overrideSorting = true;
		val4.sortingLayerID = val2.sortingLayerID;
		val4.sortingOrder = val2.sortingOrder - 1;
		_image = _root.AddComponent<Image>();
		((Graphic)_image).raycastTarget = false;
		((Behaviour)_image).enabled = false;
		_texWidth = -1f;
		MakeTexture();
		_shown = 0f;
		_flash = 0f;
		_lastHealth = -1f;
	}

	private void MakeTexture()
	{
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Expected O, but got Unknown
		//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b7: 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)
		_texWidth = _width.Value;
		if ((Object)(object)_tex == (Object)null)
		{
			_tex = new Texture2D(256, 256, (TextureFormat)4, false);
			((Texture)_tex).wrapMode = (TextureWrapMode)1;
			((Texture)_tex).filterMode = (FilterMode)1;
		}
		float num = Mathf.Clamp(1f - _texWidth, 0.15f, 0.85f);
		Color32[] array = (Color32[])(object)new Color32[65536];
		for (int i = 0; i < 256; i++)
		{
			for (int j = 0; j < 256; j++)
			{
				float num2 = ((float)j + 0.5f) / 256f * 2f - 1f;
				float num3 = ((float)i + 0.5f) / 256f * 2f - 1f;
				float num4 = Mathf.Pow(Mathf.Pow(Mathf.Abs(num2), 3f) + Mathf.Pow(Mathf.Abs(num3), 3f), 1f / 3f);
				float num5 = Mathf.Clamp01((num4 - num) / (1.05f - num));
				num5 = num5 * num5 * (3f - 2f * num5);
				ref Color32 reference = ref array[i * 256 + j];
				reference = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)Mathf.RoundToInt(num5 * 255f));
			}
		}
		_tex.SetPixels32(array);
		_tex.Apply(false);
		if ((Object)(object)_sprite == (Object)null)
		{
			_sprite = Sprite.Create(_tex, new Rect(0f, 0f, 256f, 256f), new Vector2(0.5f, 0.5f));
		}
		if ((Object)(object)_image != (Object)null)
		{
			_image.sprite = _sprite;
		}
	}

	private void PlayBeat(float hp)
	{
		if ((Object)(object)_audio == (Object)null)
		{
			_audio = ((Component)this).gameObject.AddComponent<AudioSource>();
			_audio.playOnAwake = false;
			_audio.spatialBlend = 0f;
			AudioMixerGroup val = SfxGroup();
			if ((Object)(object)val != (Object)null)
			{
				_audio.outputAudioMixerGroup = val;
			}
		}
		if ((Object)(object)_beat == (Object)null)
		{
			_beat = MakeBeat();
		}
		_audio.pitch = Mathf.Lerp(1.08f, 0.96f, Mathf.Clamp01(hp / Mathf.Max(0.01f, _soundThreshold.Value / 100f)));
		_audio.PlayOneShot(_beat, _soundVolume.Value);
		if (!_soundLogged)
		{
			_soundLogged = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Heartbeat sound playing (mixer group: " + (((Object)(object)_audio.outputAudioMixerGroup != (Object)null) ? ((Object)_audio.outputAudioMixerGroup).name : "none") + ")."));
		}
	}

	private static AudioMixerGroup SfxGroup()
	{
		AudioMan instance = AudioMan.instance;
		if ((Object)(object)instance == (Object)null)
		{
			return null;
		}
		if ((Object)(object)instance.m_masterMixer != (Object)null)
		{
			AudioMixerGroup[] array = instance.m_masterMixer.FindMatchingGroups("SFX");
			if (array != null && array.Length > 0)
			{
				return array[0];
			}
		}
		return instance.m_guiMixer;
	}

	private static AudioClip MakeBeat()
	{
		int num = 24255;
		float[] array = new float[num];
		Thump(array, 44100, 0f, 1f);
		Thump(array, 44100, 0.2f, 0.65f);
		float num2 = 0f;
		for (int i = 0; i < num; i++)
		{
			num2 = Mathf.Max(num2, Mathf.Abs(array[i]));
		}
		if (num2 > 0.95f)
		{
			for (int j = 0; j < num; j++)
			{
				array[j] *= 0.95f / num2;
			}
		}
		AudioClip val = AudioClip.Create("HealthPulseBeat", num, 1, 44100, false);
		val.SetData(array, 0);
		return val;
	}

	private static void Thump(float[] s, int rate, float at, float gain)
	{
		int num = (int)(at * (float)rate);
		int num2 = (int)(0.25f * (float)rate);
		double num3 = 0.0;
		Random random = new Random(7);
		float num4 = 0f;
		for (int i = 0; i < num2 && num + i < s.Length; i++)
		{
			float num5 = (float)i / (float)rate;
			float num6 = Mathf.Lerp(110f, 60f, Mathf.Clamp01(num5 / 0.09f));
			num3 += Math.PI * 2.0 * (double)num6 / (double)rate;
			float num7 = Mathf.Clamp01(num5 / 0.004f) * Mathf.Exp((0f - num5) / 0.07f);
			float num8 = (float)(Math.Sin(num3) + 0.5 * Math.Sin(2.0 * num3) + 0.22 * Math.Sin(3.0 * num3));
			num4 += ((float)random.NextDouble() * 2f - 1f - num4) * 0.08f;
			float num9 = num4 * 2.5f * Mathf.Exp((0f - num5) / 0.012f);
			s[num + i] += (num8 * num7 + num9) * gain * 0.55f;
		}
	}
}