RUMBLE does not support other mod managers. If you want to use a manager, you must use the RUMBLE Mod Manager, a manager specifically designed for this game.
Decompiled source of FuryoftheFallen v1.0.1
Mods/Fury of the Fallen.dll
Decompiled 8 hours agousing 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 System.Text; using FuryOfTheFallen; using HarmonyLib; using Il2CppRUMBLE.Managers; using Il2CppRUMBLE.Players.Subsystems; using Il2CppRUMBLE.Utilities; using MelonLoader; using Microsoft.CodeAnalysis; using RumbleModUI; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(FuryOfTheFallenMod), "Fury of the Fallen", "1.0.1", "Nano", null)] [assembly: MelonColor(127, 52, 235, 131)] [assembly: MelonGame("Buckethead Entertainment", "RUMBLE")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace FuryOfTheFallen { public sealed class FuryOfTheFallenMod : MelonMod { [CompilerGenerated] private sealed class <HeartbeatLoop>d__74 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public FuryOfTheFallenMod <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <HeartbeatLoop>d__74(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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) int num = <>1__state; FuryOfTheFallenMod furyOfTheFallenMod = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; break; case 1: <>1__state = -1; break; } if (furyOfTheFallenMod._furyVisualShouldBeActive && furyOfTheFallenMod._heartbeatCall != null && furyOfTheFallenMod._audioPlaySoundMethod != null) { Vector3 position = (((Object)(object)furyOfTheFallenMod._playerHead != (Object)null) ? furyOfTheFallenMod._playerHead.position : Vector3.zero); try { object[] parameters = furyOfTheFallenMod.BuildPlaySoundArgs(furyOfTheFallenMod._audioPlaySoundMethod, furyOfTheFallenMod._heartbeatCall, position); furyOfTheFallenMod._heartbeatSource = furyOfTheFallenMod._audioPlaySoundMethod.Invoke(null, parameters); } catch (Exception ex) { ((MelonBase)furyOfTheFallenMod).LoggerInstance.Warning("Heartbeat play failed: " + ex.Message); } <>2__current = (object)new WaitForSeconds(0.85f); <>1__state = 1; return true; } furyOfTheFallenMod._heartbeatLoopRunning = false; furyOfTheFallenMod._heartbeatSource = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private const int EnabledSettingIndex = 0; private const int AlwaysOnSettingIndex = 1; private const int ActivationHealthSettingIndex = 2; private const int OverlayStrengthSettingIndex = 3; private const int PanicScaleAmountSettingIndex = 4; private const float OverlayFadeSpeed = 3.5f; private const float HmdMaskScaleMultiplier = 0.2f; private const float DefaultPanicScaleStrength = 0.15f; private const float PanicScaleSpeed = 8.5f; private const float HeartbeatVolume = 0.8f; private readonly Mod _modUi = new Mod(); private readonly List<ModSetting> _settings = new List<ModSetting>(); private bool _effectEnabled = true; private bool _alwaysOn; private int _activationHealth = 1; private float _overlayStrength = 0.85f; private float _panicScaleStrength = 0.15f; private short _lastKnownHealth = short.MaxValue; private bool _furyVisualShouldBeActive; private float _currentOverlayAlpha; private bool? _lastLoggedActivationState; private bool? _lastLoggedVisualVisibility; private float _nextModUiRegisterTryTime; private bool _modUiRegistered; private bool _modUiBuilt; private float _nextReferenceCheckTime; private Transform _playerHead; private bool _speedLinesReady; private float _animatedPhase; private GameObject _furySpeedLinesRoot; private GameObject _furySpeedLinesOverlay; private GameObject _furyHmdSpeedLinesOverlay; private Material _speedLinesMaterial; private Material _hmdSpeedLinesMaterial; private Vector3 _furyFlatBaseScale = Vector3.one; private Vector3 _furyHmdBaseScale = Vector3.one; private object _heartbeatCall; private object _heartbeatSource; private bool _heartbeatLoopRunning; private string _heartbeatPath; private string _heartbeatRelativePath; private MethodInfo _audioCreateCallMethod; private MethodInfo _audioPlaySoundMethod; private float _nextHeartbeatInitTryTime; internal static FuryOfTheFallenMod Instance { get; private set; } public override void OnInitializeMelon() { Instance = this; ((MelonBase)this).HarmonyInstance.PatchAll(typeof(LocalHealthPatch)); ((MelonBase)this).LoggerInstance.Msg("Harmony patch applied: PlayerHealth.SetHealth"); } public override void OnLateInitializeMelon() { TrySubscribeUiInitialized(); LoadHeartbeatAudio(); } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { _nextReferenceCheckTime = 0f; _speedLinesReady = false; _playerHead = null; _lastLoggedVisualVisibility = null; _nextHeartbeatInitTryTime = 0f; } public override void OnSceneWasUnloaded(int buildIndex, string sceneName) { _furyVisualShouldBeActive = false; _lastKnownHealth = short.MaxValue; _lastLoggedActivationState = null; _lastLoggedVisualVisibility = null; StopHeartbeat(); ApplySpeedLinesVisuals(0f); } public override void OnUpdate() { if (!_modUiRegistered && _modUiBuilt && Time.unscaledTime >= _nextModUiRegisterTryTime) { TryRegisterModUi(); _nextModUiRegisterTryTime = Time.unscaledTime + 1f; } if (!_speedLinesReady) { EnsureSpeedLinesClone(); } if (Time.unscaledTime >= _nextReferenceCheckTime) { RefreshPlayerHeadReference(); _nextReferenceCheckTime = Time.unscaledTime + 1f; } float num = (_furyVisualShouldBeActive ? _overlayStrength : 0f); _currentOverlayAlpha = Mathf.MoveTowards(_currentOverlayAlpha, num, 3.5f * Time.unscaledDeltaTime); float num2 = (_furyVisualShouldBeActive ? (0.88f + 0.12f * Mathf.Sin(Time.unscaledTime * 9f)) : 1f); float alpha = _currentOverlayAlpha * num2; if (_furyVisualShouldBeActive) { _animatedPhase += Time.unscaledDeltaTime; } else { StopHeartbeat(); } if (_furyVisualShouldBeActive) { EnsureHeartbeatAudioCall(); EnsureHeartbeatLoop(); } UpdateHmdOverlayPose(); ApplySpeedLinesVisuals(alpha); } internal void HandleHealthChanged(PlayerHealth health, short newHealth, short previousHealth, bool useEffects) { if (!((Object)(object)health == (Object)null) && IsLocalPlayerHealth(health)) { _lastKnownHealth = newHealth; RecomputeActivationState(); } } private void SetupModUi() { //IL_004e: 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_005f: Expected O, but got Unknown //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Expected O, but got Unknown //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Expected O, but got Unknown //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Expected O, but got Unknown //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Expected O, but got Unknown if (!_modUiBuilt) { _modUi.ModName = "Fury of the Fallen"; _modUi.ModVersion = "1.0.1"; _modUi.SetFolder("FuryOfTheFallen"); _modUi.AddDescription("Summary", "", "Uses SpeedEffects lines, tinted red, on low health.", new Tags { IsSummary = true }); _settings.Clear(); _settings.Add((ModSetting)_modUi.AddToList("Enable Fury Visual", true, 0, "Toggles the visual effect.", new Tags())); _settings.Add((ModSetting)_modUi.AddToList("Always On", false, 0, "When enabled, the effect is always active regardless of health.", new Tags())); _settings.Add((ModSetting)_modUi.AddToList("Fury Visual Activation Health", 1, "Turns on when health is less than or equal to this value.", new Tags())); _settings.Add((ModSetting)_modUi.AddToList("Overlay Strength", 0.85f, "Opacity of the red speed lines. 0 to 1.", new Tags())); _settings.Add((ModSetting)_modUi.AddToList("Panic Scale Amount", 0.15f, "How strong the scale is. 0 to 0.5", new Tags())); _modUi.GetFromFile(); _modUi.ModSaved += SaveSettings; _modUiBuilt = true; } } private void OnUiInitialized() { SetupModUi(); SaveSettings(); TryRegisterModUi(); } private void TrySubscribeUiInitialized() { try { if (UI.instance != null) { UI.instance.UI_Initialized -= OnUiInitialized; UI.instance.UI_Initialized += OnUiInitialized; } } catch (Exception) { } } private void TryRegisterModUi() { if (_modUiRegistered || !_modUiBuilt || UI.instance == null) { return; } try { UI.instance.AddMod(_modUi); _modUiRegistered = true; ((MelonBase)this).LoggerInstance.Msg("Fury of the Fallen: registered in ModUI."); } catch (Exception) { } } private void SaveSettings() { if (_settings.Count >= 5) { _effectEnabled = ReadBool(_settings[0].SavedValue, fallback: true); _alwaysOn = ReadBool(_settings[1].SavedValue, fallback: false); _activationHealth = Mathf.Max(1, ReadInt(_settings[2].SavedValue, 1)); _overlayStrength = Mathf.Clamp01(ReadFloat(_settings[3].SavedValue, 0.85f)); _panicScaleStrength = Mathf.Clamp(ReadFloat(_settings[4].SavedValue, 0.15f), 0f, 0.5f); _settings[2].Value = _activationHealth; _settings[2].SavedValue = _activationHealth; _settings[3].Value = _overlayStrength; _settings[3].SavedValue = _overlayStrength; _settings[4].Value = _panicScaleStrength; _settings[4].SavedValue = _panicScaleStrength; RecomputeActivationState(); } } private void RecomputeActivationState() { bool furyVisualShouldBeActive = _furyVisualShouldBeActive; bool flag = _lastKnownHealth > 0 && _lastKnownHealth <= _activationHealth; _furyVisualShouldBeActive = _effectEnabled && (_alwaysOn || flag); if (!_lastLoggedActivationState.HasValue || _lastLoggedActivationState.Value != _furyVisualShouldBeActive || furyVisualShouldBeActive != _furyVisualShouldBeActive) { ((MelonBase)this).LoggerInstance.Msg($"Fury visual {(_furyVisualShouldBeActive ? "ENABLED" : "DISABLED")} | reason={GetActivationReason(flag)} | health={_lastKnownHealth} threshold={_activationHealth}"); _lastLoggedActivationState = _furyVisualShouldBeActive; } } private string GetActivationReason(bool lowHealthActive) { if (!_effectEnabled) { return "setting_disabled"; } if (_alwaysOn) { return "always_on"; } if (!lowHealthActive) { return "health_above_threshold"; } return "low_health"; } private void RefreshPlayerHeadReference() { try { PlayerManager instance = Singleton<PlayerManager>.Instance; if ((Object)(object)instance == (Object)null || instance.localPlayer == null || (Object)(object)instance.localPlayer.Controller == (Object)null || (Object)(object)instance.localPlayer.Controller.PlayerCamera == (Object)null) { _playerHead = null; } else { _playerHead = ((Component)instance.localPlayer.Controller.PlayerCamera).transform; } } catch { _playerHead = null; } } private void UpdateHmdOverlayPose() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_furyHmdSpeedLinesOverlay == (Object)null) && !((Object)(object)_playerHead == (Object)null)) { _furyHmdSpeedLinesOverlay.transform.position = _playerHead.position; _furyHmdSpeedLinesOverlay.transform.rotation = _playerHead.rotation * Quaternion.Euler(0f, -90f, 0f); } } private void EnsureSpeedLinesClone() { //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Expected O, but got Unknown //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Expected O, but got Unknown if (_speedLinesReady && (Object)(object)_furySpeedLinesRoot != (Object)null && (Object)(object)_speedLinesMaterial != (Object)null && (Object)(object)_hmdSpeedLinesMaterial != (Object)null) { return; } GameObject val = GameObject.Find("Speed Lines"); if ((Object)(object)val == (Object)null) { return; } try { _furySpeedLinesRoot = Object.Instantiate<GameObject>(val); ((Object)_furySpeedLinesRoot).name = "Fury Speed Lines"; Object.DontDestroyOnLoad((Object)(object)_furySpeedLinesRoot); _furySpeedLinesOverlay = ((Component)_furySpeedLinesRoot.transform.GetChild(0)).gameObject; _furyHmdSpeedLinesOverlay = ((Component)_furySpeedLinesRoot.transform.GetChild(1)).gameObject; _furyFlatBaseScale = _furySpeedLinesOverlay.transform.localScale; _furyHmdBaseScale = _furyHmdSpeedLinesOverlay.transform.localScale; Image component = ((Component)_furySpeedLinesOverlay.transform.GetChild(0)).GetComponent<Image>(); MeshRenderer component2 = ((Component)_furyHmdSpeedLinesOverlay.transform.GetChild(0)).GetComponent<MeshRenderer>(); if (!((Object)(object)component == (Object)null) && !((Object)(object)component2 == (Object)null) && !((Object)(object)((Graphic)component).material == (Object)null) && !((Object)(object)((Renderer)component2).material == (Object)null)) { _speedLinesMaterial = new Material(((Graphic)component).material); _hmdSpeedLinesMaterial = new Material(((Renderer)component2).material); ((Graphic)component).material = _speedLinesMaterial; ((Renderer)component2).material = _hmdSpeedLinesMaterial; _speedLinesMaterial.SetFloat("_RenderSpace", 1f); ((Component)_furyHmdSpeedLinesOverlay.transform.GetChild(0)).gameObject.layer = LayerMask.NameToLayer("PlayerFade"); _speedLinesReady = true; } } catch (Exception) { } } private void ApplySpeedLinesVisuals(float alpha) { //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) if (_speedLinesReady && !((Object)(object)_speedLinesMaterial == (Object)null) && !((Object)(object)_hmdSpeedLinesMaterial == (Object)null)) { bool flag = alpha > 0.001f; if (!_lastLoggedVisualVisibility.HasValue || _lastLoggedVisualVisibility.Value != flag) { ((MelonBase)this).LoggerInstance.Msg($"Speed lines {(flag ? "ENABLED" : "DISABLED")} | alpha={alpha:0.000}"); _lastLoggedVisualVisibility = flag; } if ((Object)(object)_furySpeedLinesOverlay != (Object)null) { _furySpeedLinesOverlay.SetActive(flag); } if ((Object)(object)_furyHmdSpeedLinesOverlay != (Object)null) { _furyHmdSpeedLinesOverlay.SetActive(flag); } if (!flag) { SetBothMaterials("_MaskScale", 10f); SetBothMaterials("_Colour", new Color(1f, 0f, 0f, 0f)); ApplyPanicScale(1f); } else { float num = 0.5f + 0.5f * Mathf.Sin(_animatedPhase * 8.5f); float value = Mathf.Lerp(0.95f, 0.58f, num); SetBothMaterials("_MaskScale", value); SetBothMaterials("_Colour", new Color(1f, 0f, 0f, Mathf.Clamp01(alpha))); ApplyPanicScale(1f + _panicScaleStrength * (0.5f + 0.5f * Mathf.Sin(_animatedPhase * 8.5f))); } } } private void ApplyPanicScale(float multiplier) { //IL_001a: 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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_furySpeedLinesOverlay != (Object)null) { _furySpeedLinesOverlay.transform.localScale = _furyFlatBaseScale * multiplier; } if ((Object)(object)_furyHmdSpeedLinesOverlay != (Object)null) { _furyHmdSpeedLinesOverlay.transform.localScale = _furyHmdBaseScale * multiplier; } } private void SetBothMaterials(string property, float value) { _speedLinesMaterial.SetFloat(property, value); _hmdSpeedLinesMaterial.SetFloat(property, value * 0.2f); } private void SetBothMaterials(string property, Color value) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) _speedLinesMaterial.SetColor(property, value); _hmdSpeedLinesMaterial.SetColor(property, value); } private bool IsLocalPlayerHealth(PlayerHealth health) { try { PlayerManager instance = Singleton<PlayerManager>.Instance; if ((Object)(object)instance == (Object)null || instance.localPlayer == null || (Object)(object)instance.localPlayer.Controller == (Object)null) { return false; } PlayerHealth component = ((Component)instance.localPlayer.Controller).GetComponent<PlayerHealth>(); return (Object)(object)component != (Object)null && (Object)(object)component == (Object)(object)health; } catch { return false; } } private void LoadHeartbeatAudio() { try { string text = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty; string path = Path.GetDirectoryName(text) ?? text; string text2 = Path.Combine(path, "UserData", "FuryOfTheFallen"); Directory.CreateDirectory(text2); string[] obj = new string[4] { Path.Combine(text, "Sounds"), Path.Combine(path, "Mods", "Sounds"), Path.Combine(path, "UserData", "FuryOfTheFallen", "Sounds"), "C:\\Users\\nanol\\Downloads\\Rumble Stuff\\Rumble Mod\\Fury of the Fallen\\Sounds" }; string text3 = null; string[] array = obj; foreach (string path2 in array) { if (Directory.Exists(path2)) { string[] files = Directory.GetFiles(path2, "*.wav"); if (files.Length != 0) { text3 = files[0]; break; } string[] files2 = Directory.GetFiles(path2, "*.mp3"); if (files2.Length != 0) { text3 = files2[0]; break; } } } if (text3 == null) { ((MelonBase)this).LoggerInstance.Warning("Heartbeat source audio not found. Expected .wav or .mp3 in Sounds folder."); return; } string text4 = Path.Combine(text2, Path.GetFileName(text3)); if (!File.Exists(text4)) { File.Copy(text3, text4, overwrite: true); ((MelonBase)this).LoggerInstance.Msg("Copied heartbeat audio to UserData: " + text4); } else { FileInfo fileInfo = new FileInfo(text3); FileInfo fileInfo2 = new FileInfo(text4); if (fileInfo.Length != fileInfo2.Length || fileInfo.LastWriteTimeUtc > fileInfo2.LastWriteTimeUtc) { File.Copy(text3, text4, overwrite: true); ((MelonBase)this).LoggerInstance.Msg("Updated heartbeat audio in UserData: " + text4); } } _heartbeatPath = text4; _heartbeatRelativePath = Path.Combine("UserData", "FuryOfTheFallen", Path.GetFileName(text4)); if (!IsValidWavFile(_heartbeatPath)) { string text5 = Path.Combine(text2, "heartbeat_auto.wav"); WriteFallbackHeartbeatWav(text5); _heartbeatPath = text5; _heartbeatRelativePath = Path.Combine("UserData", "FuryOfTheFallen", Path.GetFileName(text5)); ((MelonBase)this).LoggerInstance.Warning("Heartbeat source was not a valid WAV file. Generated fallback heartbeat_auto.wav."); } ((MelonBase)this).LoggerInstance.Msg("Heartbeat file ready: " + _heartbeatPath); } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Warning("Heartbeat load failed: " + ex.Message); } } private static bool IsValidWavFile(string path) { try { if (!File.Exists(path)) { return false; } using FileStream fileStream = File.OpenRead(path); if (fileStream.Length < 12) { return false; } byte[] array = new byte[12]; if (fileStream.Read(array, 0, 12) < 12) { return false; } string @string = Encoding.ASCII.GetString(array, 0, 4); string string2 = Encoding.ASCII.GetString(array, 8, 4); return @string == "RIFF" && string2 == "WAVE"; } catch { return false; } } private static void WriteFallbackHeartbeatWav(string path) { int num = Mathf.CeilToInt(17640f); short[] array = new short[num]; AddPulse(array, 22050, 0.06f, 0.1f, 70f, 0.85f); AddPulse(array, 22050, 0.3f, 0.08f, 62f, 0.7f); Directory.CreateDirectory(Path.GetDirectoryName(path) ?? "."); using FileStream output = File.Create(path); using BinaryWriter binaryWriter = new BinaryWriter(output); int num2 = num * 2; int value = 44100; short value2 = 2; binaryWriter.Write(Encoding.ASCII.GetBytes("RIFF")); binaryWriter.Write(36 + num2); binaryWriter.Write(Encoding.ASCII.GetBytes("WAVE")); binaryWriter.Write(Encoding.ASCII.GetBytes("fmt ")); binaryWriter.Write(16); binaryWriter.Write((short)1); binaryWriter.Write((short)1); binaryWriter.Write(22050); binaryWriter.Write(value); binaryWriter.Write(value2); binaryWriter.Write((short)16); binaryWriter.Write(Encoding.ASCII.GetBytes("data")); binaryWriter.Write(num2); for (int i = 0; i < num; i++) { binaryWriter.Write(array[i]); } } private static void AddPulse(short[] buffer, int sampleRate, float startSeconds, float lengthSeconds, float frequency, float amplitude) { int num = Mathf.Max(0, Mathf.FloorToInt(startSeconds * (float)sampleRate)); int num2 = Mathf.Max(1, Mathf.FloorToInt(lengthSeconds * (float)sampleRate)); int num3 = Mathf.Min(buffer.Length, num + num2); for (int i = num; i < num3; i++) { float num4 = (float)(i - num) / (float)num2; float num5 = 1f - num4; float num6 = Mathf.Sin(2f * Mathf.PI * frequency * ((float)(i - num) / (float)sampleRate)) * num5 * amplitude; int num7 = buffer[i] + Mathf.RoundToInt(num6 * 32767f * 0.55f); buffer[i] = (short)Mathf.Clamp(num7, -32768, 32767); } } private void EnsureHeartbeatLoop() { if (!_heartbeatLoopRunning && _heartbeatCall != null && !(_audioPlaySoundMethod == null)) { _heartbeatLoopRunning = true; MelonCoroutines.Start(HeartbeatLoop()); } } [IteratorStateMachine(typeof(<HeartbeatLoop>d__74))] private IEnumerator HeartbeatLoop() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <HeartbeatLoop>d__74(0) { <>4__this = this }; } private void StopHeartbeat() { if (_heartbeatSource != null) { try { _heartbeatSource.GetType().GetMethod("ReturnToPool", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(_heartbeatSource, null); } catch { } _heartbeatSource = null; } _heartbeatLoopRunning = false; } private void EnsureHeartbeatAudioCall() { if (_heartbeatCall != null || Time.unscaledTime < _nextHeartbeatInitTryTime) { return; } _nextHeartbeatInitTryTime = Time.unscaledTime + 2f; if (string.IsNullOrWhiteSpace(_heartbeatPath) || !File.Exists(_heartbeatPath)) { LoadHeartbeatAudio(); if (string.IsNullOrWhiteSpace(_heartbeatPath) || !File.Exists(_heartbeatPath)) { ((MelonBase)this).LoggerInstance.Warning("Heartbeat missing: no audio file in UserData\\FuryOfTheFallen."); return; } } CacheAudioManagerMethods(); if (_audioCreateCallMethod == null) { ((MelonBase)this).LoggerInstance.Warning("Heartbeat unavailable: no compatible CreateAudioCall method found yet."); return; } if (_audioPlaySoundMethod == null) { ((MelonBase)this).LoggerInstance.Warning("Heartbeat unavailable: no compatible PlaySound method found yet."); return; } try { string text = ((!string.IsNullOrWhiteSpace(_heartbeatRelativePath)) ? _heartbeatRelativePath : _heartbeatPath); object[] parameters = BuildCreateAudioCallArgs(_audioCreateCallMethod, text); _heartbeatCall = _audioCreateCallMethod.Invoke(null, parameters); if (_heartbeatCall == null && !string.Equals(text, _heartbeatPath, StringComparison.OrdinalIgnoreCase)) { object[] parameters2 = BuildCreateAudioCallArgs(_audioCreateCallMethod, _heartbeatPath); _heartbeatCall = _audioCreateCallMethod.Invoke(null, parameters2); } if (_heartbeatCall == null) { ((MelonBase)this).LoggerInstance.Warning($"Heartbeat create call returned null for: {text} and {_heartbeatPath}. Try .wav if .mp3 fails."); } else { ((MelonBase)this).LoggerInstance.Msg("Heartbeat audio loaded: " + text); } } catch (TargetInvocationException ex) when (ex.InnerException != null) { ((MelonBase)this).LoggerInstance.Warning("Heartbeat create inner failure: " + ex.InnerException.GetType().Name + ": " + ex.InnerException.Message); } catch (Exception ex2) { ((MelonBase)this).LoggerInstance.Warning("Heartbeat create call failed: " + ex2.Message); } } private void CacheAudioManagerMethods() { if (_audioCreateCallMethod != null && _audioPlaySoundMethod != null) { return; } List<MethodInfo> list = new List<MethodInfo>(); List<MethodInfo> list2 = new List<MethodInfo>(); Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Type[] array; try { array = assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { array = Array.FindAll(ex.Types, (Type t) => t != null); } Type[] array2 = array; foreach (Type type in array2) { if (type == null || type.Name.IndexOf("AudioManager", StringComparison.OrdinalIgnoreCase) < 0) { continue; } MethodInfo[] methods; try { methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } catch { continue; } MethodInfo[] array3 = methods; foreach (MethodInfo methodInfo in array3) { ParameterInfo[] parameters; Type returnType; try { parameters = methodInfo.GetParameters(); returnType = methodInfo.ReturnType; } catch { continue; } if (parameters.Length != 0) { if (methodInfo.Name.IndexOf("CreateAudioCall", StringComparison.OrdinalIgnoreCase) >= 0 && parameters[0].ParameterType == typeof(string) && returnType != typeof(void)) { list.Add(methodInfo); } else if (methodInfo.Name.IndexOf("PlaySound", StringComparison.OrdinalIgnoreCase) >= 0 && Array.Exists(parameters, (ParameterInfo a) => a.ParameterType == typeof(Vector3))) { list2.Add(methodInfo); } } } } } MethodInfo methodInfo2 = null; MethodInfo audioPlaySoundMethod = null; List<MethodInfo> list3 = list.FindAll((MethodInfo m) => m.DeclaringType != null && m.DeclaringType.FullName != null && m.DeclaringType.FullName.IndexOf("Il2CppRUMBLE.Audio.AudioManager", StringComparison.OrdinalIgnoreCase) >= 0); List<MethodInfo> list4 = list2.FindAll((MethodInfo m) => m.DeclaringType != null && m.DeclaringType.FullName != null && m.DeclaringType.FullName.IndexOf("Il2CppRUMBLE.Audio.AudioManager", StringComparison.OrdinalIgnoreCase) >= 0); foreach (MethodInfo item in (list3.Count > 0) ? list3 : list) { foreach (MethodInfo item2 in (list4.Count > 0) ? list4 : list2) { if (!(item2.DeclaringType != item.DeclaringType)) { ParameterInfo[] parameters2; try { parameters2 = item2.GetParameters(); } catch { continue; } if (parameters2.Length != 0 && parameters2[0].ParameterType == item.ReturnType) { methodInfo2 = item; audioPlaySoundMethod = item2; break; } } } if (methodInfo2 != null) { break; } } if (methodInfo2 == null) { foreach (MethodInfo item3 in (list3.Count > 0) ? list3 : list) { foreach (MethodInfo item4 in (list4.Count > 0) ? list4 : list2) { if (!(item4.DeclaringType != item3.DeclaringType)) { ParameterInfo[] parameters3; try { parameters3 = item4.GetParameters(); } catch { continue; } if (parameters3.Length != 0 && parameters3[0].ParameterType.Name.IndexOf("AudioCall", StringComparison.OrdinalIgnoreCase) >= 0) { methodInfo2 = item3; audioPlaySoundMethod = item4; break; } } } if (methodInfo2 != null) { break; } } } if (methodInfo2 == null && list.Count > 0) { methodInfo2 = ((list3.Count > 0) ? list3[0] : list[0]); foreach (MethodInfo item5 in (list4.Count > 0) ? list4 : list2) { ParameterInfo[] parameters4; try { parameters4 = item5.GetParameters(); } catch { continue; } if (parameters4.Length != 0 && parameters4[0].ParameterType.Name.IndexOf("AudioCall", StringComparison.OrdinalIgnoreCase) >= 0) { audioPlaySoundMethod = item5; break; } } } _audioCreateCallMethod = methodInfo2; _audioPlaySoundMethod = audioPlaySoundMethod; if (_audioCreateCallMethod != null) { ((MelonBase)this).LoggerInstance.Msg("Heartbeat found create method: " + _audioCreateCallMethod.DeclaringType?.FullName + "." + _audioCreateCallMethod.Name); } if (_audioPlaySoundMethod != null) { ((MelonBase)this).LoggerInstance.Msg("Heartbeat found play method: " + _audioPlaySoundMethod.DeclaringType?.FullName + "." + _audioPlaySoundMethod.Name); } } private object[] BuildCreateAudioCallArgs(MethodInfo method, string path) { ParameterInfo[] parameters = method.GetParameters(); object[] array = new object[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { Type parameterType = parameters[i].ParameterType; if (i == 0 && parameterType == typeof(string)) { array[i] = path; } else if (parameterType == typeof(float)) { array[i] = 0.8f; } else if (parameterType == typeof(bool)) { array[i] = false; } else { array[i] = (parameters[i].HasDefaultValue ? parameters[i].DefaultValue : GetDefault(parameterType)); } } return array; } private object[] BuildPlaySoundArgs(MethodInfo method, object audioCall, Vector3 position) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) ParameterInfo[] parameters = method.GetParameters(); object[] array = new object[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { Type parameterType = parameters[i].ParameterType; if (i == 0) { array[i] = audioCall; } else if (parameterType == typeof(Vector3)) { array[i] = position; } else if (parameterType == typeof(bool)) { array[i] = false; } else if (parameterType == typeof(float)) { array[i] = 0.8f; } else { array[i] = (parameters[i].HasDefaultValue ? parameters[i].DefaultValue : GetDefault(parameterType)); } } return array; } private static object GetDefault(Type type) { if (!type.IsValueType) { return null; } return Activator.CreateInstance(type); } private static int ReadInt(object value, int fallback) { if (value is int) { return (int)value; } if (value is float num) { return Mathf.RoundToInt(num); } if (!int.TryParse(value?.ToString(), out var result)) { return fallback; } return result; } private static float ReadFloat(object value, float fallback) { if (value is float) { return (float)value; } if (value is int num) { return num; } if (!float.TryParse(value?.ToString(), out var result)) { return fallback; } return result; } private static bool ReadBool(object value, bool fallback) { if (value is bool) { return (bool)value; } if (!bool.TryParse(value?.ToString(), out var result)) { return fallback; } return result; } } [HarmonyPatch(typeof(PlayerHealth), "SetHealth", new Type[] { typeof(short), typeof(short), typeof(bool) })] internal static class LocalHealthPatch { private static void Postfix(ref PlayerHealth __instance, short newHealth, short previousHealth, bool useEffects) { FuryOfTheFallenMod.Instance?.HandleHealthChanged(__instance, newHealth, previousHealth, useEffects); } } }