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 ValheimBoombox v0.2.8
BepInEx/plugins/Rizias-ValheimBoombox/ValheimBoombox.dll
Decompiled a week 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.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SoftReferenceableAssets; using TMPro; using UnityEngine; using UnityEngine.Animations; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.UI; using UnityEngine.Playables; using UnityEngine.Rendering; using UnityEngine.UI; using ValheimBoombox.Visuals; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ValheimBoombox.Visuals { internal sealed class BassShockwaveEdge { private const int Segments = 96; private const int Rows = 5; private readonly Mesh _mesh; private readonly MeshRenderer _renderer; private readonly Vector3[] _vertices = (Vector3[])(object)new Vector3[485]; private readonly Color32[] _colors = (Color32[])(object)new Color32[485]; private readonly float[] _rowPosition = new float[5] { 0f, 0.22f, 0.62f, 0.91f, 1f }; private readonly float[] _rowHeight = new float[5] { 0f, 0.3f, 1f, 0.25f, 0f }; private readonly float[] _rowAlpha = new float[5] { 0f, 0.32f, 1f, 0.6f, 0f }; private readonly Material _material; public BassShockwaveEdge(Transform parent, Material source, Material fallback, Color tint, List<Object> owned) { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: 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_00d8: Expected O, but got Unknown //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Expected O, but got Unknown //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Expected O, but got Unknown //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) Material val = source; if ((Object)(object)val == (Object)null || (Object)(object)val.shader == (Object)null || !val.shader.isSupported || ((Object)val.shader).name == "Hidden/InternalErrorShader") { val = fallback; } if ((Object)(object)val == (Object)null) { throw new InvalidOperationException("A supported transparent material is required for the bass front."); } Material val2 = (_material = new Material(val) { name = "Bass narrow textured edge", hideFlags = (HideFlags)52 }); owned.Add((Object)(object)val2); if ((Object)(object)source != (Object)null && source.HasProperty("_MainTex") && val2.HasProperty("_MainTex")) { val2.SetTexture("_MainTex", source.GetTexture("_MainTex")); Texture texture = source.GetTexture("_MainTex"); val2.mainTextureScale = new Vector2(((Object)(object)texture != (Object)null && (int)texture.wrapMode == 0) ? 3f : 1f, 1f); val2.mainTextureOffset = Vector2.zero; } SetTint(tint); val2.renderQueue = 3010; Vector2[] array = (Vector2[])(object)new Vector2[_vertices.Length]; Vector3[] array2 = (Vector3[])(object)new Vector3[_vertices.Length]; int[] array3 = new int[2304]; for (int i = 0; i < 5; i++) { for (int j = 0; j <= 96; j++) { int num = i * 97 + j; array[num] = new Vector2((float)j / 96f, 0.35f + (float)i / 4f * 0.65f); array2[num] = Vector3.up; if (i != 4 && j != 96) { int num2 = (i * 96 + j) * 6; array3[num2] = num; array3[num2 + 1] = num + 1; array3[num2 + 2] = num + 96 + 1; array3[num2 + 3] = num + 1; array3[num2 + 4] = num + 96 + 2; array3[num2 + 5] = num + 96 + 1; } } } _mesh = new Mesh { name = "Bass textured front edge", hideFlags = (HideFlags)52 }; _mesh.MarkDynamic(); _mesh.vertices = _vertices; _mesh.normals = array2; _mesh.uv = array; _mesh.triangles = array3; owned.Add((Object)(object)_mesh); GameObject val3 = new GameObject("Narrow textured front") { hideFlags = (HideFlags)52 }; val3.transform.SetParent(parent, false); val3.AddComponent<MeshFilter>().sharedMesh = _mesh; _renderer = val3.AddComponent<MeshRenderer>(); ((Renderer)_renderer).sharedMaterial = val2; ((Renderer)_renderer).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)_renderer).receiveShadows = false; ((Renderer)_renderer).lightProbeUsage = (LightProbeUsage)0; ((Renderer)_renderer).reflectionProbeUsage = (ReflectionProbeUsage)0; } public void Update(float radius, float maxRadius, float age) { //IL_0133: 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_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0167: 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) float num = Mathf.Min(Mathf.Lerp(0.15f, 0.25f, Mathf.Clamp01(radius / maxRadius)), radius * 0.4f); float num2 = radius - num; float num3 = Mathf.Min(0.075f, num * 0.3f); float num4 = Mathf.SmoothStep(0f, 1f, age / 0.065f); float num5 = 1f - Mathf.SmoothStep(0f, 1f, Mathf.InverseLerp(0.58f, 1f, age)); for (int i = 0; i < 5; i++) { float num6 = Mathf.Lerp(num2, radius, _rowPosition[i]); for (int j = 0; j <= 96; j++) { float num7 = (float)j / 96f * (float)Math.PI * 2f; float num8 = 0.85f + 0.15f * Mathf.Sin(num7 * 9f + age * 5f); float num9 = num3 * _rowHeight[i] * num8; num9 = Mathf.Min(num9, Mathf.Sqrt(Mathf.Max(0f, radius * radius - num6 * num6)) * 0.8f); int num10 = i * 97 + j; _vertices[num10] = new Vector3(Mathf.Cos(num7) * num6, num9, Mathf.Sin(num7) * num6); _colors[num10] = Color32.op_Implicit(new Color(1f, 1f, 1f, _rowAlpha[i] * num4 * num5)); } } _mesh.SetVertices(_vertices); _mesh.SetColors(_colors); _mesh.RecalculateBounds(); ((Renderer)_renderer).enabled = radius > 0.005f && num4 * num5 > 0.001f; } public void SetVisible(bool visible) { ((Renderer)_renderer).enabled = visible && ((Renderer)_renderer).enabled; } public void SetTint(Color tint) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) string text = (_material.HasProperty("_TintColor") ? "_TintColor" : (_material.HasProperty("_Color") ? "_Color" : null)); if (text != null) { _material.SetColor(text, new Color(tint.r, tint.g, tint.b, 0.6f * tint.a)); } } } public sealed class BassShockwaveVisual : MonoBehaviour { public const string DefaultResourceName = "ValheimBoombox.BassEffects.bundle"; public float Intensity = 1f; public Quaternion FrontRotation = Quaternion.identity; private Color _color; private static AssetBundle _bundle; private static GameObject _magicPrefab; private readonly List<Object> _owned = new List<Object>(8); private Transform _root; private BassShockwaveEdge _front; private bool _initialized; public void SetColor(Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) _color = color; } public static bool LoadEmbeddedAssets(Assembly assembly, string resourceName = "ValheimBoombox.BassEffects.bundle") { if ((Object)(object)_bundle != (Object)null) { return true; } if (assembly == null) { throw new ArgumentNullException("assembly"); } using Stream stream = assembly.GetManifestResourceStream(resourceName); if (stream == null) { return false; } using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); return LoadAssets(memoryStream.ToArray()); } public static bool LoadAssets(byte[] bytes) { if ((Object)(object)_bundle != (Object)null) { return true; } if (bytes == null || bytes.Length == 0) { return false; } AssetBundle val = AssetBundle.LoadFromMemory(bytes); if ((Object)(object)val == (Object)null) { return false; } GameObject val2 = val.LoadAsset<GameObject>("bass-magicblast"); if ((Object)(object)val2 == (Object)null) { val.Unload(true); return false; } _bundle = val; _magicPrefab = val2; return true; } public static void ReleaseAssets() { if ((Object)(object)_bundle != (Object)null) { _bundle.Unload(false); } _bundle = null; _magicPrefab = null; } public void Init(Color color, Material alphaMaterial) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0061: 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) //IL_0088: Unknown result type (might be due to invalid IL or missing references) if (_initialized) { throw new InvalidOperationException("Init must be called once per bass wave."); } if ((Object)(object)_magicPrefab == (Object)null) { throw new InvalidOperationException("Load the BassEffects asset bundle before creating a bass wave."); } GameObject val = new GameObject("Bass native pressure front") { hideFlags = (HideFlags)52 }; _root = val.transform; _root.position = ((Component)this).transform.position; _color = color; Material sharedMaterial = ((Renderer)_magicPrefab.GetComponentInChildren<ParticleSystemRenderer>(true)).sharedMaterial; _front = new BassShockwaveEdge(_root, sharedMaterial, alphaMaterial, WithIntensity(Intensity), _owned); _initialized = true; ((Component)_root).gameObject.SetActive(((Behaviour)this).isActiveAndEnabled); } public void UpdateRadius(float radius, float maxRadius, float normalizedAge) { //IL_004e: 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_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) if (_initialized && Finite(radius) && Finite(maxRadius) && Finite(normalizedAge)) { radius = Mathf.Max(0f, radius); maxRadius = Mathf.Max(0.01f, maxRadius); float age = Mathf.Clamp01(normalizedAge); _root.position = ((Component)this).transform.position; _root.rotation = FrontRotation; _root.localScale = Vector3.one; _front.SetTint(WithIntensity(Intensity)); _front.Update(radius, maxRadius, age); _front.SetVisible(Intensity > 0f); } } private static bool Finite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } private Color WithIntensity(float value) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) return new Color(_color.r, _color.g, _color.b, _color.a * Mathf.Max(0f, value)); } private void OnEnable() { if ((Object)(object)_root != (Object)null) { ((Component)_root).gameObject.SetActive(true); } } private void OnDisable() { if ((Object)(object)_root != (Object)null) { ((Component)_root).gameObject.SetActive(false); } } private void OnDestroy() { if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)((Component)_root).gameObject); } for (int i = 0; i < _owned.Count; i++) { if (_owned[i] != (Object)null) { Object.Destroy(_owned[i]); } } _owned.Clear(); } } } namespace VikingBoombox { public sealed class AudioDeck : MonoBehaviour { private const double EndToleranceSeconds = 0.025; private const float GlowGain = 3f; private const float GlowShare = 0.3f; public DeckState State; public AudioSource Source; public GameObject Visual; public BoomboxDisplay Display; public BoomboxMount Mount; public BoomboxAmplifier Amplifier; public BoomboxPlatformAnchor PlatformAnchor; public BoomboxTrophyVisuals TrophyVisuals; public BoomboxVisualPresenter VisualPresenter; public ItemData Item; public ItemDrop WorldDrop; public bool StoragePaused; private BoomboxMessage status = new BoomboxMessage("text.no_track_selected"); public bool Loading; public int EmittedWaves; private int generation; private CancellationTokenSource preparation; private Task<MusicLiveSource> preparationTask; private Coroutine opening; private const double MembranePushSeconds = 0.15; private float bass; private float membraneLevel; private double membranePushUntil = double.NegativeInfinity; private float glowLevel; private readonly BoomboxMembraneMeter membranes = new BoomboxMembraneMeter(); private bool simulationPaused; private bool started; private bool wasPlaying; private bool audioChanged; private BoomboxDesktopAudio desktopAudio; private long playedFrame; private double playedDsp; private string preparedTitle; private string preparedArtist; private int seekSettledFrame; private MusicLiveStream stream; private MusicLiveStream retiringStream; private bool buffering; private float? pendingSeek; private readonly BoomboxSyntheticBeatClock synthetic = new BoomboxSyntheticBeatClock(); private readonly MusicEffectFeed feed = new MusicEffectFeed(); private MusicEffectRunner effect; public string Status { get { return status.ToString(); } set { status = BoomboxMessage.Raw(value); } } public double LastBeatTime { get; private set; } public float LastBeatStrength { get; private set; } public string TrackArtist { get { if (!string.IsNullOrEmpty(preparedArtist)) { return preparedArtist; } return BoomboxPlugin.Instance.FindTrack(State.TrackId)?.artist; } } public bool IsActivePlayback { get { if (State.insertedCassette != null && !StoragePaused && (Object)(object)Source != (Object)null && Source.isPlaying && State.playing && !Loading && !simulationPaused && Time.timeScale > 0f && !AudioListener.pause && (Object)(object)BoomboxPlugin.Instance != (Object)null) { return BoomboxPlugin.Instance.AudioWorldActive; } return false; } } public bool IsSyntheticActive { get { if (BoomboxSyntheticTrackId.IsSynthetic(State.TrackId) && State.insertedCassette != null && !StoragePaused && State.playing && !Loading && !simulationPaused && Time.timeScale > 0f && !AudioListener.pause && (Object)(object)BoomboxPlugin.Instance != (Object)null) { return BoomboxPlugin.Instance.AudioWorldActive; } return false; } } public string TrackTitle { get { if (!string.IsNullOrEmpty(preparedTitle)) { return preparedTitle; } return BoomboxPlugin.Instance.FindTrack(State.TrackId)?.title; } } public string TrackLabel { get { if (!string.IsNullOrEmpty(preparedTitle)) { if (!string.IsNullOrEmpty(preparedArtist)) { return preparedArtist + " — " + preparedTitle; } return preparedTitle; } return BoomboxPlugin.Instance.FindTrack(State.TrackId)?.Label; } } public long AudioBufferBytes => stream?.BufferBytes ?? 0; public MusicLiveStream Stream => stream; public float VisualStrength => membraneLevel; public float GlowStrength => glowLevel; public float TrackSeconds { get { if (stream == null) { return 0f; } return (float)stream.Seconds; } } public MusicEffectRunner Effect => effect; public MusicMoment Moment => feed.Moment; private static double Threshold => BoomboxPlugin.Instance.Settings.Effect.Current.Threshold.Value; public void SetStatus(string key, params object[] arguments) { status = new BoomboxMessage(key, arguments); } public void Initialize(DeckState state) { //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Expected O, but got Unknown State = state; Visual = VikingBoomboxVisual.Create(((Component)this).transform); Display = ((Component)this).gameObject.AddComponent<BoomboxDisplay>(); Display.Initialize(); Mount = ((Component)this).gameObject.AddComponent<BoomboxMount>(); Mount.Initialize(); Source = ((Component)this).gameObject.AddComponent<AudioSource>(); Source.playOnAwake = false; Source.spatialBlend = (state.onBack ? 0f : 1f); Source.reverbZoneMix = 0f; Source.bypassReverbZones = true; Source.dopplerLevel = 0f; Source.rolloffMode = (AudioRolloffMode)1; Source.minDistance = 2f; Source.maxDistance = BoomboxPlugin.Instance.SoundDistance.Value; Source.spread = 35f; Source.priority = 80; Source.volume = (state.muted ? 0f : state.volume); desktopAudio = ((Component)this).gameObject.AddComponent<BoomboxDesktopAudio>(); ConnectMixer(); Amplifier = ((Component)this).gameObject.AddComponent<BoomboxAmplifier>(); Amplifier.Initialize(this); PlatformAnchor = ((Component)this).gameObject.AddComponent<BoomboxPlatformAnchor>(); PlatformAnchor.Initialize(this); VisualPresenter = ((Component)this).gameObject.AddComponent<BoomboxVisualPresenter>(); VisualPresenter.Initialize(State, Visual); TrophyVisuals = VisualPresenter.Decorations; ((Component)this).gameObject.AddComponent<BoomboxEnchantment>().Initialize(this); ((Component)this).gameObject.AddComponent<BoomboxBoatHelp>().Initialize(this); effect = ((Component)this).gameObject.AddComponent<MusicEffectRunner>(); effect.Initialize(this); AudioSettings.OnAudioConfigurationChanged += new AudioConfigurationChangeHandler(OnAudioChanged); if (state.TrackId != null) { LoadTrack(); } } public void TogglePlayback() { if (!State.playing && State.insertedCassette == null) { SetStatus("text.insert_a_cassette_2"); BoomboxPlugin.Instance.Notice = Status; return; } Capture(); State.playing = !State.playing; if ((Object)(object)Source.clip != (Object)null) { if (State.playing) { PlayAtSavedPosition(); } else { Source.Pause(); } } else if (State.playing && !Loading) { LoadTrack(); } BoomboxPlugin.Instance.Save(); } public void SelectTrack(int index) { if (State.tracks.Count != 0) { State.trackIndex = (index % State.tracks.Count + State.tracks.Count) % State.tracks.Count; State.seconds = 0f; LoadTrack(); BoomboxPlugin.Instance.Save(); } } public bool Seek(float seconds) { if ((Object)(object)Source == (Object)null || (Object)(object)Source.clip == (Object)null || stream == null || Loading || float.IsNaN(seconds) || float.IsInfinity(seconds)) { return false; } float num = Mathf.Clamp(seconds, 0f, Mathf.Max(0f, TrackSeconds - 0.02f)); Source.Pause(); buffering = true; pendingSeek = num; stream.Seek(FrameAt(num)); State.seconds = num; seekSettledFrame = Time.frameCount + 2; bass = 0f; ForgetMembranes(); feed.Reset(); LastBeatTime = -1.0; if (!State.playing || StoragePaused || simulationPaused || Time.timeScale <= 0f || AudioListener.pause) { Source.Pause(); } BoomboxPlugin.Instance.Save(); return true; } private void PlayAtSavedPosition() { if (Loading || (Object)(object)Source.clip == (Object)null) { return; } if (State.insertedCassette == null || StoragePaused || Time.timeScale <= 0f || AudioListener.pause) { simulationPaused = true; return; } if (!stream.Ready) { buffering = true; Source.Pause(); return; } float seconds = Resumable(State.seconds); if (started) { Source.UnPause(); } if (!Source.isPlaying) { Source.timeSamples = stream.Clip(FrameAt(seconds)); Source.Play(); } playedFrame = Played(); ForgetMembranes(); feed.Reset(); State.seconds = seconds; started = true; wasPlaying = true; playedDsp = AudioSettings.dspTime; } private void ForgetMembranes() { membraneLevel = (glowLevel = 0f); membranePushUntil = double.NegativeInfinity; membranes.Forget(); } public void Capture() { if (Time.frameCount > seekSettledFrame && (Object)(object)Source != (Object)null && (Object)(object)Source.clip != (Object)null && !Loading && Source.isPlaying) { State.seconds = Mathf.Max(State.seconds, (float)Played() / 48000f); } } private void Update() { if (State == null || (Object)(object)BoomboxPlugin.Instance == (Object)null) { return; } if (!BoomboxPlugin.Instance.AudioWorldActive) { desktopAudio?.Poll(wanted: false); membraneLevel = (glowLevel = 0f); membranes.Rest(); if ((Object)(object)Visual != (Object)null) { Visual.GetComponent<VikingBoomboxVisualState>().ApplyMembranes(0f); } return; } if (audioChanged) { Revive(); } ConnectMixer(); Source.volume = (State.muted ? 0f : State.volume); Source.spatialBlend = (State.onBack ? 0f : 1f); Source.maxDistance = BoomboxPlugin.Instance.SoundDistance.Value; bool flag = State.TrackId == "system:computer-audio"; BoomboxMusicTap desktopTap = desktopAudio.Poll(flag && IsSyntheticActive); if (flag) { Status = desktopAudio.Status; } bool paused = SynchronizePlaybackPause(); UpdateStreamReadiness(paused); AnalyzePlayback(flag, desktopTap, paused); PresentPlayback(); } private bool SynchronizePlaybackPause() { bool flag = State.insertedCassette == null || StoragePaused || Time.timeScale <= 0f || AudioListener.pause; if (flag && !simulationPaused) { Capture(); Source.Pause(); simulationPaused = true; wasPlaying = false; } else if (!flag && simulationPaused) { simulationPaused = false; if (State.playing && (Object)(object)Source.clip != (Object)null && !Loading) { PlayAtSavedPosition(); } } return flag; } private void UpdateStreamReadiness(bool paused) { if (stream != null && stream.Error != null) { string error = stream.Error; Source.Stop(); if ((Object)(object)Source.clip != (Object)null) { Object.Destroy((Object)(object)Source.clip); } Source.clip = null; ReleaseStream(); buffering = false; Fail(error); } if (stream == null || !buffering || !stream.Ready) { return; } if (Loading) { Loading = false; SetStatus("text.ready"); } if (pendingSeek.HasValue) { int num = stream.Clip(FrameAt(pendingSeek.Value)); if (Source.timeSamples == num) { Source.timeSamples = (num + 1) % stream.Frames; } Source.timeSamples = num; pendingSeek = null; } buffering = false; if (State.playing && !paused) { PlayAtSavedPosition(); } } private void AnalyzePlayback(bool computerAudio, BoomboxMusicTap desktopTap, bool paused) { bass = Mathf.MoveTowards(bass, 0f, Time.deltaTime * 3.5f); feed.Rest(); if (IsActivePlayback && (Object)(object)Source.clip != (Object)null && Time.frameCount > seekSettledFrame) { Capture(); wasPlaying = true; playedFrame = Played(); playedDsp = AudioSettings.dspTime; membranes.Measure(stream, playedFrame, Source.clip.channels, Source.clip.frequency, stream.Length); feed.Track(stream, playedFrame, Threshold); membranes.MeasureWave(stream, (long)Math.Floor(feed.Moment.Time * 48000.0)); if (stream.EofFrame >= 0 && playedFrame >= stream.EofFrame) { Advance(); } else if (Source.isVirtual && stream.EofFrame < 0 && playedFrame >= MusicLiveStream.FramesOf(stream.Seconds)) { Advance(); } } else if (IsSyntheticActive) { if (computerAudio) { if ((Object)(object)desktopTap != (Object)null) { feed.Tap(desktopTap, Threshold); membranes.MeasureDesktop(desktopTap, Time.unscaledTime, 1f); } else { feed.Rest(); membranes.Rest(); } } else { synthetic.Advance(feed, State.TrackId, Threshold); if (BoomboxSyntheticTrackId.IsGameMusic(State.TrackId)) { membranes.MeasureGameMusic(Time.unscaledTime, AudioSettings.outputSampleRate); } } } else if (started && wasPlaying && State.playing && !Loading && !buffering && (Object)(object)Source.clip != (Object)null && !paused && !Source.isPlaying) { wasPlaying = false; if (ReachedEnd()) { Advance(); } } } private void PresentPlayback() { bool num = IsActivePlayback || IsSyntheticActive; Player localPlayer = Player.m_localPlayer; bool flag = num && (Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead() && !((Character)localPlayer).IsTeleporting(); PresentMusic(flag); membraneLevel = ((!flag) ? 0f : ((feed.Moment.Time < membranePushUntil) ? 1f : membranes.Travel)); glowLevel = (flag ? BoomboxAudioEnvelope.Follow(glowLevel, Mathf.Max(Mathf.Min(1f, membranes.Level * 3f) * 0.3f, bass), Time.unscaledDeltaTime) : 0f); if (!flag) { membranes.Rest(); } VisualPresenter.PresentFrame(flag, membraneLevel, glowLevel); Display.Show(this, glowLevel); } private bool ReachedEnd() { double num = (double)playedFrame + Math.Max(0.0, AudioSettings.dspTime - playedDsp) * (double)Source.clip.frequency; long num2 = ((stream.EofFrame >= 0) ? stream.EofFrame : stream.Length); return num + 0.025 * (double)Source.clip.frequency >= (double)num2; } private void Advance() { State.seconds = 0f; if (!State.repeat && State.trackIndex >= State.tracks.Count - 1) { State.playing = false; } SelectTrack(State.trackIndex + 1); } private void ConnectMixer() { if ((Object)(object)Source.outputAudioMixerGroup == (Object)null && (Object)(object)MusicMan.instance != (Object)null) { Source.outputAudioMixerGroup = MusicMan.instance.m_musicMixer; } } private void OnDestroy() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown AudioSettings.OnAudioConfigurationChanged -= new AudioConfigurationChangeHandler(OnAudioChanged); generation++; CancelPreparation(); if ((Object)(object)Source != (Object)null) { Source.Stop(); } ReleaseStream(); if ((Object)(object)Source != (Object)null && (Object)(object)Source.clip != (Object)null) { Object.Destroy((Object)(object)Source.clip); Source.clip = null; } if (preparationTask == null) { return; } try { if (!preparationTask.Wait(2250)) { BoomboxPlugin.Instance?.LogError("Audio track preparation shutdown timed out"); } } catch (AggregateException) { } } private void PresentMusic(bool active) { if (active) { foreach (MusicStrike item in feed.Moment.Fresh) { OnBeat(item.Strength, item.Time); } } effect.Step(feed.Moment, active); BoomboxRemoteNetwork.Observe(this, active); } internal void OnBeat(float strength, double timestamp) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) bass = Mathf.Max(bass, strength); membranePushUntil = Math.Max(membranePushUntil, timestamp + 0.15); LastBeatTime = timestamp; LastBeatStrength = strength; Vector3 origin = ((Component)this).transform.position + ((Component)this).transform.up * 0.2f; EmittedWaves++; BassWave.Emit(origin, State.upgradeTier, strength); VisualPresenter?.OnMusicEvent(strength); try { BoomboxSpeakerPulse.Emit(Visual.GetComponent<VikingBoomboxVisualState>(), strength); } catch (Exception ex) { BoomboxPlugin.Instance.LogError(BoomboxText.Get("text.speaker_effect_unavailable_the_music_pulse_was") + ex.Message); } } private void OnAudioChanged(bool deviceWasChanged) { audioChanged = true; } public void LoadTrack() { CancelPreparation(); preparation = new CancellationTokenSource(); int marker = ++generation; Source.Stop(); bool num = ReleaseStream(); buffering = false; pendingSeek = null; if ((Object)(object)Source.clip != (Object)null) { Object.Destroy((Object)(object)Source.clip); } Source.clip = null; started = false; wasPlaying = false; preparedTitle = null; preparedArtist = null; ForgetMembranes(); feed.Reset(); synthetic.Reset(); if (!num) { Fail(BoomboxText.Get("error.stream_shutdown_timeout")); } else if (State.insertedCassette == null) { State.playing = false; SetStatus("text.the_boombox_is_not_ready_for_playback_yet"); Loading = false; } else if (State.TrackId == null) { SetStatus("text.no_track_selected"); Loading = false; } else if (!State.playing) { SetStatus("text.press_play_to_prepare_the_track"); Loading = false; } else if (BoomboxSyntheticTrackId.IsSynthetic(State.TrackId)) { SetStatus("text.ready"); Loading = false; } else { opening = ((MonoBehaviour)this).StartCoroutine(Open(marker, State.TrackId, preparation.Token)); } } private IEnumerator Open(int marker, string trackId, CancellationToken cancellation) { Loading = true; SetStatus("text.preparing_track"); BoomboxPlugin plugin = BoomboxPlugin.Instance; Stopwatch retiring = Stopwatch.StartNew(); while (preparationTask != null && !preparationTask.IsCompleted) { if (marker != generation || cancellation.IsCancellationRequested) { yield break; } if (retiring.ElapsedMilliseconds >= 2000) { Fail(BoomboxText.Get("error.previous_track_cleanup_pending")); yield break; } yield return null; } if (marker != generation || cancellation.IsCancellationRequested) { yield break; } Task<MusicLiveSource> task = (preparationTask = Task.Run(() => MusicPreparation.Locate(plugin, trackId, cancellation), cancellation)); MusicProcess.Observe(task); while (!task.IsCompleted) { yield return null; } if (marker != generation) { yield break; } if (task.IsCanceled) { Loading = false; yield break; } if (task.IsFaulted) { Fail(task.Exception.GetBaseException().Message); yield break; } MusicLiveSource result = task.Result; preparedTitle = result.Title; preparedArtist = result.Artist; plugin.UpdateTrackMetadata(trackId, result.Title, result.Artist); long frame; try { int num = default(int); int num2 = default(int); AudioSettings.GetDSPBufferSize(ref num, ref num2); long num3 = MusicLiveStream.FramesOf(result.Duration); frame = Math.Min(Math.Max(0L, FrameAt(State.seconds)), Math.Max(0L, num3 - 960)); stream = new MusicLiveStream(result, plugin.Settings.AudioBufferSeconds.Value, checked(num * num2)); } catch (Exception ex) { Fail(ex.Message); yield break; } yield return Attach(marker, trackId, frame); } private IEnumerator Attach(int marker, string trackId, long frame) { if (marker != generation || stream == null) { yield break; } State.seconds = (float)frame / 48000f; frame = FrameAt(State.seconds); int clipFrame = stream.Clip(frame); if (clipFrame == 0) { stream.Begin(frame); while (marker == generation && stream != null && stream.Error == null && !stream.Ready) { yield return null; } if (marker != generation || stream == null || stream.Error != null) { yield break; } } try { Source.clip = AudioClip.Create(trackId, stream.Frames, 2, 48000, true, new PCMReaderCallback(stream.Read), new PCMSetPositionCallback(stream.SetPosition)); ((Object)Source.clip).name = trackId; Source.loop = stream.Looping; if (clipFrame > 0) { stream.Begin(frame); Source.timeSamples = clipFrame; } } catch (Exception ex) { ReleaseStream(); if ((Object)(object)Source.clip != (Object)null) { Object.Destroy((Object)(object)Source.clip); Source.clip = null; } Fail(ex.Message); yield break; } buffering = true; } private void Revive() { audioChanged = false; desktopAudio?.ResetDevice(); if (!((Object)(object)Source.clip == (Object)null)) { long frame = FrameAt(Resumable(State.seconds)); string name = ((Object)Source.clip).name; Source.Stop(); Object.Destroy((Object)(object)Source.clip); Source.clip = null; stream.Hold(); feed.Reset(); started = (wasPlaying = false); if (opening != null) { ((MonoBehaviour)this).StopCoroutine(opening); } opening = ((MonoBehaviour)this).StartCoroutine(Attach(generation, name, frame)); } } private void CancelPreparation() { if (opening != null) { ((MonoBehaviour)this).StopCoroutine(opening); opening = null; } CancellationTokenSource previous = preparation; preparation = null; if (previous == null) { return; } previous.Cancel(); if (preparationTask == null || preparationTask.IsCompleted) { previous.Dispose(); return; } preparationTask.ContinueWith(delegate(Task<MusicLiveSource> done) { _ = done.Exception; previous.Dispose(); }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } private bool ReleaseStream() { if (retiringStream != null) { if (!retiringStream.Stopped) { return false; } retiringStream = null; } MusicLiveStream musicLiveStream = stream; stream = null; if (musicLiveStream == null) { return true; } musicLiveStream.Dispose(); if (musicLiveStream.CleanupError != null) { BoomboxPlugin.Instance?.LogError(musicLiveStream.CleanupError); } if (musicLiveStream.Stopped) { return true; } retiringStream = musicLiveStream; return false; } private float Resumable(float seconds) { return Mathf.Max(0f, stream.EndKnown ? Mathf.Min(seconds, TrackSeconds - 0.02f) : seconds); } private static long FrameAt(float seconds) { return (long)Math.Round((double)seconds * 48000.0); } private long Played() { return stream.Track(Source.timeSamples); } private void Fail(string error) { Loading = false; State.playing = false; SetStatus("status.playback_error", error); BoomboxPlugin.Instance.LogError(Status); } } internal sealed class BackSurfaceSampler : IDisposable { private readonly Renderer renderer; private readonly SkinnedMeshRenderer skinned; private readonly MeshFilter filter; private readonly List<Vector3> vertices = new List<Vector3>(); private readonly List<Vector3> projected = new List<Vector3>(); private readonly Vector3[] clipA = (Vector3[])(object)new Vector3[12]; private readonly Vector3[] clipB = (Vector3[])(object)new Vector3[12]; private Mesh baked; private Mesh original; private int[] triangles = new int[0]; public bool Active { get { if ((Object)(object)renderer != (Object)null && renderer.enabled) { return ((Component)renderer).gameObject.activeInHierarchy; } return false; } } public BackSurfaceSampler(Renderer source) { renderer = source; skinned = (SkinnedMeshRenderer)(object)((source is SkinnedMeshRenderer) ? source : null); filter = ((Component)source).GetComponent<MeshFilter>(); } public static bool Supports(Renderer renderer) { if (!(renderer is SkinnedMeshRenderer)) { if (renderer is MeshRenderer) { return (Object)(object)((Component)renderer).GetComponent<MeshFilter>() != (Object)null; } return false; } return true; } public bool Prepare() { //IL_00b9: 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_00ce: Expected O, but got Unknown if ((Object)(object)renderer == (Object)null || !renderer.enabled || !((Component)renderer).gameObject.activeInHierarchy) { vertices.Clear(); return false; } Mesh val = (((Object)(object)skinned != (Object)null) ? skinned.sharedMesh : (((Object)(object)filter != (Object)null) ? filter.sharedMesh : null)); if ((Object)(object)val == (Object)null) { vertices.Clear(); return false; } bool flag = (Object)(object)val != (Object)(object)original; original = val; if ((Object)(object)skinned != (Object)null) { if ((Object)(object)baked == (Object)null) { baked = new Mesh { name = "Boombox surface snapshot" }; baked.MarkDynamic(); } skinned.BakeMesh(baked, true); baked.GetVertices(vertices); if (flag || triangles.Length == 0) { triangles = baked.triangles; } } else if (val.isReadable) { val.GetVertices(vertices); if (flag || triangles.Length == 0) { triangles = val.triangles; } } else { Mesh val2 = BoomboxHeadPlacement.CollisionMesh(filter); val2.GetVertices(vertices); if (flag || triangles.Length == 0) { triangles = val2.triangles; } } return true; } public bool Sample(Matrix4x4 worldToFrame, Rect area, out float depth, bool prepared = false) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) depth = float.NegativeInfinity; if ((!prepared && !Prepare()) || vertices.Count == 0 || (Object)(object)renderer == (Object)null) { return false; } Project(worldToFrame); bool result = false; for (int i = 0; i + 2 < triangles.Length; i += 3) { int num = ClipTriangle(i, area); for (int j = 0; j < num; j++) { if (!float.IsNaN(clipA[j].z) && !float.IsInfinity(clipA[j].z)) { depth = Mathf.Max(depth, clipA[j].z); result = true; } } } return result; } public void BlockedOffsets(Matrix4x4 worldToFrame, Rect area, float rear, float front, float clearance, List<Vector2> result) { //IL_001d: 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_00b6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)renderer == (Object)null || vertices.Count == 0) { return; } Project(worldToFrame); for (int i = 0; i + 2 < triangles.Length; i += 3) { int num = ClipTriangle(i, area); if (num != 0) { float num2 = float.PositiveInfinity; float num3 = float.NegativeInfinity; for (int j = 0; j < num; j++) { num2 = Mathf.Min(num2, clipA[j].z); num3 = Mathf.Max(num3, clipA[j].z); } if (!float.IsInfinity(num2) && !float.IsInfinity(num3) && !float.IsNaN(num2) && !float.IsNaN(num3)) { result.Add(new Vector2(num2 - front - clearance, num3 - rear + clearance)); } } } } private void Project(Matrix4x4 worldToFrame) { //IL_0000: 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_0016: 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_0037: 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) Matrix4x4 val = worldToFrame * ((Component)renderer).transform.localToWorldMatrix; projected.Clear(); foreach (Vector3 vertex in vertices) { projected.Add(((Matrix4x4)(ref val)).MultiplyPoint3x4(vertex)); } } private int ClipTriangle(int triangle, Rect area) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_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) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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_007a: 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_009b: 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_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: 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_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011a: 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_0128: 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_0136: Unknown result type (might be due to invalid IL or missing references) int num = triangles[triangle]; int num2 = triangles[triangle + 1]; int num3 = triangles[triangle + 2]; if (num >= projected.Count || num2 >= projected.Count || num3 >= projected.Count) { return 0; } Vector3 val = projected[num]; Vector3 val2 = projected[num2]; Vector3 val3 = projected[num3]; if (Mathf.Max(val.x, Mathf.Max(val2.x, val3.x)) < ((Rect)(ref area)).xMin || Mathf.Min(val.x, Mathf.Min(val2.x, val3.x)) > ((Rect)(ref area)).xMax || Mathf.Max(val.y, Mathf.Max(val2.y, val3.y)) < ((Rect)(ref area)).yMin || Mathf.Min(val.y, Mathf.Min(val2.y, val3.y)) > ((Rect)(ref area)).yMax) { return 0; } clipA[0] = val; clipA[1] = val2; clipA[2] = val3; int count = Clip(clipA, 3, clipB, 0, ((Rect)(ref area)).xMin, greater: true); count = Clip(clipB, count, clipA, 0, ((Rect)(ref area)).xMax, greater: false); count = Clip(clipA, count, clipB, 1, ((Rect)(ref area)).yMin, greater: true); return Clip(clipB, count, clipA, 1, ((Rect)(ref area)).yMax, greater: false); } private static int Clip(Vector3[] input, int count, Vector3[] output, int axis, float boundary, bool greater) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: 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_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //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_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) if (count == 0) { return 0; } int result = 0; Vector3 val = input[count - 1]; float num = (((Vector3)(ref val))[axis] - boundary) * (greater ? 1f : (-1f)); for (int i = 0; i < count; i++) { Vector3 val2 = input[i]; float num2 = (((Vector3)(ref val2))[axis] - boundary) * (greater ? 1f : (-1f)); if (num2 >= 0f != num >= 0f) { output[result++] = Vector3.LerpUnclamped(val, val2, num / (num - num2)); } if (num2 >= 0f) { output[result++] = val2; } val = val2; num = num2; } return result; } public void Dispose() { if ((Object)(object)baked != (Object)null) { Object.Destroy((Object)(object)baked); } } } public sealed class BassWave : MonoBehaviour { public BoomboxWorldImpulse EnvironmentImpulse; private float speed; public float Radius { get; private set; } public float MaximumRadius { get; private set; } public static BassWave Emit(Vector3 origin, int tierLevel, float strength) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0011: 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) GameObject val = new GameObject("Boombox musical impulse front"); val.transform.position = origin; BassWave bassWave = val.AddComponent<BassWave>(); BoomboxSettings settings = BoomboxPlugin.Instance.Settings; bassWave.MaximumRadius = BoomboxAuraNetwork.WaveRadius(tierLevel); bassWave.speed = BoomboxSettings.PositiveOr(settings.WaveSpeed.Value, 7f); bassWave.EnvironmentImpulse = val.AddComponent<BoomboxWorldImpulse>(); bassWave.EnvironmentImpulse.Initialize(origin, bassWave.MaximumRadius, Mathf.Clamp01(strength)); return bassWave; } private void Update() { if (!(Time.timeScale <= 0f) && !AudioListener.pause) { float radius = Radius; Radius = Mathf.Min(MaximumRadius, Radius + speed * Time.deltaTime); if (BoomboxPlugin.Instance.Settings.LocalGameplayEnabled) { EnvironmentImpulse.ApplyFront(radius, Radius); } if (Radius >= MaximumRadius) { Object.Destroy((Object)(object)((Component)this).gameObject); } } } } public sealed class BoomboxAmplifier : MonoBehaviour { private AudioDeck deck; private BoomboxAmplifierVisual appearance; public string UpgradeMessage { get; private set; } = ""; public UpgradeTier Tier => BoomboxProgression.GetTier(deck.State.upgradeTier); public void Initialize(AudioDeck ownerDeck) { deck = ownerDeck; appearance = ((Component)this).gameObject.AddComponent<BoomboxAmplifierVisual>(); appearance.Initialize(deck); } public UpgradePlan GetUpgradePlan() { return PlanFor(Tier.Level, Player.m_localPlayer); } public static UpgradePlan PlanFor(int currentTier, Player player) { Dictionary<string, int> inventory = CountInventory(player); if (currentTier >= BoomboxProgression.EnabledMaxTier) { return BoomboxProgression.PlanNextUpgrade(currentTier, null, 0, inventory); } UpgradeTier tier = BoomboxProgression.GetTier(currentTier + 1); CraftingStation val = FindStation(player, tier.Recipe); return BoomboxProgression.PlanNextUpgrade(currentTier, ((Object)(object)val != (Object)null) ? tier.Recipe.StationPrefab : null, ((Object)(object)val != (Object)null) ? val.GetLevel(true) : 0, inventory); } public bool TryUpgrade() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !BoomboxPlugin.Instance.CanControl(deck)) { UpgradeMessage = BoomboxText.Get("text.move_closer_to_your_boombox"); return false; } UpgradePlan upgradePlan = GetUpgradePlan(); if (!upgradePlan.Allowed) { UpgradeMessage = Describe(upgradePlan); return false; } if (!Spend(localPlayer, upgradePlan.TargetTier.Recipe)) { UpgradeMessage = BoomboxText.Get("text.not_enough_materials"); return false; } deck.State.upgradeTier = upgradePlan.TargetTier.Level; appearance.Refresh(); BoomboxPlugin.Instance.Save(); if ((Object)(object)Game.instance != (Object)null) { Game.instance.SavePlayerProfile(false, false); } UpgradeMessage = BoomboxText.Get("text.amplifier_upgraded_to_level") + Tier.Level + "."; ((Character)localPlayer).Message((MessageType)2, UpgradeMessage, 0, (Sprite)null, false); return true; } public static bool Spend(Player player, UpgradeRecipe recipe) { return SpendExcept(player, recipe); } public static bool SpendExcept(Player player, UpgradeRecipe recipe, params ItemData[] excluded) { Inventory inventory = ((Humanoid)player).GetInventory(); Dictionary<string, int> dictionary = recipe.Ingredients.ToDictionary<UpgradeIngredient, string, int>((UpgradeIngredient i) => i.Prefab, (UpgradeIngredient i) => i.Amount, StringComparer.Ordinal); List<KeyValuePair<ItemData, int>> list = new List<KeyValuePair<ItemData, int>>(); ItemData[] array = inventory.GetAllItems().ToArray(); foreach (ItemData val in array) { if (!excluded.Contains(val) && !((Object)(object)val.m_dropPrefab == (Object)null) && dictionary.TryGetValue(((Object)val.m_dropPrefab).name, out var value) && value > 0) { int num2 = Math.Min(val.m_stack, value); list.Add(new KeyValuePair<ItemData, int>(val, num2)); dictionary[((Object)val.m_dropPrefab).name] -= num2; } } if (dictionary.Values.Any((int v) => v > 0)) { return false; } foreach (KeyValuePair<ItemData, int> item in list) { inventory.RemoveItem(item.Key, item.Value); } return true; } internal static CraftingStation FindStation(Player player, UpgradeRecipe recipe) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { return null; } GameObject prefab = ZNetScene.instance.GetPrefab(recipe.StationPrefab); CraftingStation val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<CraftingStation>() : null); if ((Object)(object)val == (Object)null) { return null; } List<CraftingStation> list = new List<CraftingStation>(); CraftingStation.FindStationsInRange(val.m_name, ((Component)player).transform.position, float.MaxValue, list); return (from s in list where (Object)(object)s != (Object)null && s.InUseDistance((Humanoid)(object)player) && s.CheckUsable(player, false) orderby s.GetLevel(true) descending select s).FirstOrDefault(); } public static Dictionary<string, int> CountInventory(Player player) { Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal); if ((Object)(object)player == (Object)null) { return dictionary; } foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems()) { if (!((Object)(object)allItem.m_dropPrefab == (Object)null)) { string name = ((Object)allItem.m_dropPrefab).name; dictionary[name] = (dictionary.TryGetValue(name, out var value) ? value : 0) + allItem.m_stack; } } return dictionary; } public static string ItemName(string prefab) { GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(prefab) : null); ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null); if (!((Object)(object)val2 != (Object)null)) { return prefab; } return Localization.instance.Localize(val2.m_itemData.m_shared.m_name); } public static string StationName(string prefab) { GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefab) : null); CraftingStation val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<CraftingStation>() : null); if (!((Object)(object)val2 != (Object)null)) { return prefab; } return Localization.instance.Localize(val2.m_name); } public static string Describe(UpgradePlan plan) { return plan.Status switch { UpgradeStatus.Ready => BoomboxText.Get("text.all_requirements_met"), UpgradeStatus.MaximumTier => BoomboxText.Get("text.maximum_amplifier_level"), UpgradeStatus.WrongStation => BoomboxText.Get("text.requires_an_accessible") + StationName(plan.TargetTier.Recipe.StationPrefab) + BoomboxText.Get("text.of_level") + plan.TargetTier.Recipe.StationLevel + ".", UpgradeStatus.StationLevelTooLow => BoomboxText.Get("text.station_level_too_low_required") + plan.TargetTier.Recipe.StationLevel + ".", UpgradeStatus.MissingMaterials => BoomboxText.Get("text.missing_materials") + string.Join(", ", plan.MissingIngredients.Select((UpgradeIngredient i) => ItemName(i.Prefab) + " × " + i.Amount)) + ".", _ => BoomboxText.Get("text.invalid_amplifier_level"), }; } private void Update() { if ((Object)(object)appearance != (Object)null) { appearance.Refresh(); } } } public sealed class BoomboxAmplifierVisual : MonoBehaviour { private AudioDeck deck; private bool refreshed; private ItemDrop currentWorldDrop; public void Initialize(AudioDeck ownerDeck) { deck = ownerDeck; Refresh(); } public void Refresh() { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: 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) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) if (!refreshed || !((Object)(object)deck.WorldDrop == (Object)(object)currentWorldDrop)) { refreshed = true; currentWorldDrop = deck.WorldDrop; VikingBoomboxVisualState component = deck.Visual.GetComponent<VikingBoomboxVisualState>(); if ((Object)(object)deck.Mount != (Object)null) { deck.Mount.RefreshModelBounds(); } BoxCollider component2 = ((Component)deck).GetComponent<BoxCollider>(); Bounds modelBounds; if ((Object)(object)component2 != (Object)null) { modelBounds = component.ModelBounds; component2.center = ((Bounds)(ref modelBounds)).center; modelBounds = component.ModelBounds; component2.size = ((Bounds)(ref modelBounds)).size; } BoxCollider val = (((Object)(object)deck.WorldDrop != (Object)null) ? ((Component)deck.WorldDrop).GetComponent<BoxCollider>() : null); if ((Object)(object)val != (Object)null) { modelBounds = component.ModelBounds; val.center = ((Bounds)(ref modelBounds)).center; modelBounds = component.ModelBounds; val.size = ((Bounds)(ref modelBounds)).size; } } } } internal static class BoomboxAudioEnvelope { public const double BassHz = 180.0; public static float BassRms(float[] samples, int channels, int sampleRate) { if (samples == null || channels <= 0 || sampleRate <= 0 || samples.Length < channels) { return 0f; } int num = samples.Length / channels; double num2 = 1.0 - Math.Exp(Math.PI * -360.0 / (double)sampleRate); double num3 = 0.0; for (int i = 0; i < channels; i++) { double num4 = 0.0; for (int j = 0; j < num; j++) { num4 += num2 * ((double)samples[j * channels + i] - num4); num3 += num4 * num4; } } double num5 = Math.Sqrt(num3 / (double)(num * channels)); if (!double.IsNaN(num5) && !double.IsInfinity(num5)) { return (float)num5; } return 0f; } public static float Follow(float current, float target, float deltaSeconds) { double num = ((target > current) ? 0.015 : 0.065); return current + (target - current) * (float)(1.0 - Math.Exp((double)(0f - Math.Max(0f, deltaSeconds)) / num)); } } internal static class BoomboxAuraCodec { internal static void Header(ZPackage package, string session, long revision) { package.Write(session); package.Write(revision); } internal static string ReadHeader(ZPackage package, out long revision) { if (package.Size() > 1048576) { throw new InvalidDataException("Boombox state packet too large"); } string text = Text(package, 64); if (!Guid.TryParseExact(text, "N", out var _)) { throw new InvalidDataException("Invalid Boombox session"); } revision = package.ReadLong(); if (revision < 1) { throw new InvalidDataException("Invalid Boombox revision"); } return text; } internal static void Sources(ZPackage package, ICollection<BoomboxAuraSource> sources) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) package.Write(sources.Count); foreach (BoomboxAuraSource source in sources) { package.Write(source.Id); package.Write(source.Owner); package.Write(source.OwnerName ?? ""); package.Write(source.Peer); package.Write(source.WorldItem); package.Write(source.Worn); package.Write(source.Active); package.Write(source.Position); int[] ranks = source.Ranks; foreach (int num in ranks) { package.Write(num); } package.Write(source.Decorations.Count); foreach (BoomboxAuraDecoration decoration in source.Decorations) { package.Write(decoration.Slot); package.Write(decoration.Prefab); package.Write(decoration.Effect); } } } internal static List<BoomboxAuraSource> ReadSources(ZPackage package) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) int num = Count(package, package.Size() / 8); List<BoomboxAuraSource> list = new List<BoomboxAuraSource>(); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); for (int i = 0; i < num; i++) { BoomboxAuraSource boomboxAuraSource = new BoomboxAuraSource { Id = Text(package, 64), Owner = package.ReadLong(), OwnerName = Text(package, 128), Peer = package.ReadLong(), WorldItem = package.ReadZDOID(), Worn = package.ReadBool(), Active = package.ReadBool(), Position = package.ReadVector3() }; if (!Guid.TryParseExact(boomboxAuraSource.Id, "N", out var _) || !hashSet.Add(boomboxAuraSource.Id) || !BoomboxAuraMath.Finite(boomboxAuraSource.Position.x) || !BoomboxAuraMath.Finite(boomboxAuraSource.Position.y) || !BoomboxAuraMath.Finite(boomboxAuraSource.Position.z)) { throw new InvalidDataException("Invalid Boombox identity or position"); } for (int j = 0; j < 5; j++) { boomboxAuraSource.Ranks[j] = package.ReadInt(); if (boomboxAuraSource.Ranks[j] < 0) { throw new InvalidDataException("Invalid cassette rank"); } } int num2 = Count(package, BoomboxDecorationCatalog.Slots.Length); HashSet<int> hashSet2 = new HashSet<int>(); for (int k = 0; k < num2; k++) { BoomboxAuraDecoration boomboxAuraDecoration = new BoomboxAuraDecoration { Slot = package.ReadInt(), Prefab = Text(package, 128), Effect = package.ReadInt() }; if (!hashSet2.Add(boomboxAuraDecoration.Slot) || boomboxAuraDecoration.Slot < 0 || boomboxAuraDecoration.Slot >= BoomboxDecorationCatalog.Slots.Length || boomboxAuraDecoration.Effect < 0 || boomboxAuraDecoration.Effect >= 5) { throw new InvalidDataException("Invalid Boombox decoration"); } boomboxAuraSource.Decorations.Add(boomboxAuraDecoration); } list.Add(boomboxAuraSource); } return list; } internal static void Balance(ZPackage package, BoomboxAuraBalance balance) { package.Write(balance.Radius); package.Write(balance.MusicOnly); package.Write(balance.Fallback ?? "Health"); float[][] array = new float[6][] { balance.Strength, balance.Cassette, balance.Decoration, balance.Initial, balance.PerRank, balance.Period }; float[] array2; for (int i = 0; i < array.Length; i++) { array2 = array[i]; foreach (float num in array2) { package.Write(num); } } array2 = balance.WaveRadius; foreach (float num2 in array2) { package.Write(num2); } } internal static BoomboxAuraBalance ReadBalance(ZPackage package) { BoomboxAuraBalance boomboxAuraBalance = new BoomboxAuraBalance { Radius = Number(package), MusicOnly = package.ReadBool(), Fallback = Text(package, 32) }; float[][] array = new float[6][] { boomboxAuraBalance.Strength, boomboxAuraBalance.Cassette, boomboxAuraBalance.Decoration, boomboxAuraBalance.Initial, boomboxAuraBalance.PerRank, boomboxAuraBalance.Period }; foreach (float[] array2 in array) { for (int j = 0; j < array2.Length; j++) { array2[j] = Number(package, array2 == boomboxAuraBalance.Initial || array2 == boomboxAuraBalance.PerRank); } } float[] period = boomboxAuraBalance.Period; for (int i = 0; i < period.Length; i++) { if (period[i] <= 0f) { throw new InvalidDataException("Invalid recovery period"); } } for (int k = 0; k < boomboxAuraBalance.WaveRadius.Length; k++) { boomboxAuraBalance.WaveRadius[k] = Number(package); } return boomboxAuraBalance; } private static float Number(ZPackage package, bool signed = false) { float num = package.ReadSingle(); if (!BoomboxAuraMath.Finite(num) || (!signed && num < 0f)) { throw new InvalidDataException("Invalid shared balance"); } return num; } private static int Count(ZPackage package, int maximum) { int num = package.ReadInt(); if (num < 0 || num > maximum) { throw new InvalidDataException("Invalid Boombox collection"); } return num; } private static string Text(ZPackage package, int maximum) { string text = package.ReadString(); if (text.Length > maximum) { throw new InvalidDataException("Invalid Boombox text"); } return text; } } public sealed class BoomboxAuraSource { public string Id; public long Owner; public string OwnerName; public long Peer; public ZDOID WorldItem; public bool Worn; public bool Active; public Vector3 Position; public readonly int[] Ranks = new int[5]; public readonly List<BoomboxAuraDecoration> Decorations = new List<BoomboxAuraDecoration>(); } public sealed class BoomboxAuraDecoration { public int Slot; public string Prefab; public int Effect; } public sealed class BoomboxAuraBalance { public float Radius = 20f; public bool MusicOnly; public readonly float[] Strength = new float[5] { 1f, 1f, 1f, 1f, 1f }; public readonly float[] Cassette = new float[5] { 0.05f, 1f, 1f, 1f, 0.05f }; public readonly float[] Decoration = new float[5] { 0.0025f, 0.1f, 0.1f, 0.1f, 0.0025f }; public readonly float[] Initial = new float[3] { 1f, 1f, 1f }; public readonly float[] PerRank = new float[3] { 1f, 1f, 1f }; public readonly float[] Period = new float[3] { 5f, 5f, 5f }; public readonly float[] WaveRadius = new float[9]; public string Fallback = "Health"; } public sealed class BoomboxAuraContribution { public string Id; public string OwnerName; public long Owner; public readonly float[] Values = new float[5]; } public sealed class BoomboxAuraAggregate { public readonly float[] Values = new float[5]; public readonly List<BoomboxAuraContribution> Contributions = new List<BoomboxAuraContribution>(); } public static class BoomboxAuraMath { public const int BonusCount = 5; public const int RecoveryCount = 3; public static string CassetteProperty(BoomboxDecorationBonus bonus) { return bonus switch { BoomboxDecorationBonus.Damage => "Red", BoomboxDecorationBonus.Health => "Green", BoomboxDecorationBonus.Stamina => "Yellow", BoomboxDecorationBonus.Eitr => "Blue", _ => "Purple", }; } public static int RecoveryIndex(BoomboxDecorationBonus bonus) { return (int)(bonus - 1); } public static BoomboxDecorationBonus RecoveryBonus(int index) { return (BoomboxDecorationBonus)(1 + index); } public static bool Finite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } public static float CassetteAmount(int index, int rank, BoomboxAuraBalance balance) { if (index == 0 || index == 4) { return (float)rank * balance.Cassette[index]; } if (rank <= 0) { return 0f; } int num = RecoveryIndex((BoomboxDecorationBonus)index); float val = balance.Initial[num] + (float)(rank - 1) * balance.PerRank[num]; return Math.Max(0f, val) * balance.Cassette[index]; } public static float DecorationAmount(int index, int count, BoomboxAuraBalance balance) { return (float)count * balance.Decoration[index]; } public static BoomboxAuraContribution Contribution(BoomboxAuraSource source, BoomboxAuraBalance balance) { BoomboxAuraContribution boomboxAuraContribution = new BoomboxAuraContribution { Id = source.Id, Owner = source.Owner, OwnerName = source.OwnerName }; int[] array = new int[5]; foreach (BoomboxAuraDecoration decoration in source.Decorations) { array[decoration.Effect]++; } for (int i = 0; i < 5; i++) { float num = CassetteAmount(i, source.Ranks[i], balance); boomboxAuraContribution.Values[i] = (num + DecorationAmount(i, array[i], balance)) * balance.Strength[i]; } return boomboxAuraContribution; } public static BoomboxAuraAggregate Aggregate(IEnumerable<BoomboxAuraSource> sources, Vector3 recipient, BoomboxAuraBalance balance) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) BoomboxAuraAggregate boomboxAuraAggregate = new BoomboxAuraAggregate(); if (balance == null || balance.MusicOnly) { return boomboxAuraAggregate; } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); foreach (BoomboxAuraSource source in sources) { if (source.Active && !(Vector3.Distance(source.Position, recipient) > balance.Radius) && hashSet.Add(source.Id)) { BoomboxAuraContribution boomboxAuraContribution = Contribution(source, balance); boomboxAuraAggregate.Contributions.Add(boomboxAuraContribution); for (int i = 0; i < 5; i++) { boomboxAuraAggregate.Values[i] += boomboxAuraContribution.Values[i]; } } } return boomboxAuraAggregate; } } public sealed class BoomboxAuraNetwork : MonoBehaviour { private readonly BoomboxAuraRegistry registry = new BoomboxAuraRegistry(); private readonly string session = Guid.NewGuid().ToString("N"); private readonly HashSet<ZRpc> registered = new HashSet<ZRpc>(); private readonly Dictionary<ZRpc, string> acceptedSessions = new Dictionary<ZRpc, string>(); private ZNet network; private long revision; private long receivedRevision; private string receivedSession; private float next; private float lastSnapshot; private List<BoomboxAuraSource> snapshot = new List<BoomboxAuraSource>(); private BoomboxAuraBalance balance; public static BoomboxAuraNetwork Current { get; private set; } public static bool CanEditBalance { get { if (!((Object)(object)ZNet.instance == (Object)null)) { return ZNet.instance.IsServer(); } return true; } } public static BoomboxAuraBalance Balance { get { if (!((Object)(object)Current != (Object)null) || Current.balance == null) { if (CanEditBalance) { if (BoomboxPlugin.Instance?.Settings == null) { return null; } return BoomboxPartySettings.Capture(BoomboxPlugin.Instance.Settings); } return null; } return Current.balance; } } public static IReadOnlyList<BoomboxAuraSource> Sources { get { if (!((Object)(object)Current == (Object)null)) { if (!Current.network.IsServer()) { if (!(Time.realtimeSinceStartup - Current.lastSnapshot <= 1.25f)) { return Array.Empty<BoomboxAuraSource>(); } return Current.snapshot; } return Current.registry.Sources; } return Array.Empty<BoomboxAuraSource>(); } } internal static float WaveRadius(int tier) { if (Balance == null) { if (!CanEditBalance) { return 0f; } return BoomboxPlugin.Instance.Settings.WaveRadius(tier); } return Balance.WaveRadius[Mathf.Clamp(tier, 0, 8)]; } internal static BoomboxAuraNetwork Attach(ZNet owner) { BoomboxAuraNetwork obj = ((Component)owner).GetComponent<BoomboxAuraNetwork>() ?? ((Component)owner).gameObject.AddComponent<BoomboxAuraNetwork>(); obj.network = owner; Current = obj; return obj; } internal void Connected(ZNetPeer peer) { if (registered.Add(peer.m_rpc)) { peer.m_rpc.Register<ZPackage>("BoomboxAuraSources", (Action<ZRpc, ZPackage>)ReceiveSources); peer.m_rpc.Register<ZPackage>("BoomboxAuraState", (Action<ZRpc, ZPackage>)ReceiveState); } } internal static void PublishImmediate() { BoomboxAuraNetwork current = Current; if (!((Object)(object)current == (Object)null) && !((Object)(object)current.network == (Object)null)) { current.revision++; if (current.network.IsServer()) { current.TickServer(current.network.GetPeers(), Time.realtimeSinceStartup); } else { current.TickClient(current.network.GetPeers()); } } } public static BoomboxAuraAggregate For(Player player) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) BoomboxPlugin instance = BoomboxPlugin.Instance; if ((Object)(object)player == (Object)null || ((Character)player).IsDead() || ((Character)player).IsTeleporting() || (Object)(object)instance == (Object)null || !instance.AudioWorldActive) { return new BoomboxAuraAggregate(); } return BoomboxAuraMath.Aggregate(Sources, ((Component)player).transform.position, Balance); } private void ReceiveSources(ZRpc rpc, ZPackage package) { if (!network.IsServer() || !BoomboxNetworkHandshake.Attach(network).Accepts(rpc)) { return; } ZNetPeer peer = ((IEnumerable<ZNetPeer>)network.GetPeers()).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer p) => p.m_rpc == rpc)); if (!BoomboxAuraSources.Identity(peer, out var character, out var player, out var name)) { return; } try { long num; string text = BoomboxAuraCodec.ReadHeader(package, out num); if (!acceptedSessions.TryGetValue(rpc, out var value) || !(value != text)) { List<BoomboxAuraSource> list = BoomboxAuraCodec.ReadSources(package); if (!list.Any((BoomboxAuraSource source) => !BoomboxAuraSources.ValidateAndNormalize(source, peer.m_uid, player, name, character)) && registry.Replace(peer.m_uid, text, num, list, Time.realtimeSinceStartup)) { acceptedSessions[rpc] = text; BoomboxMusicNetwork.SourcesReceived(peer.m_uid, list); } } } catch (Exception ex) { BoomboxPlugin.Instance?.LogError("Boombox aura source rejected: " + ex.Message); } } private void ReceiveState(ZRpc rpc, ZPackage package) { if (network.IsServer() || !BoomboxNetworkHandshake.Attach(network).Accepts(rpc) || ((IEnumerable<ZNetPeer>)network.GetPeers()).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer p) => p.m_rpc == rpc && p.m_server && p.IsReady())) == null) { return; } try { long num; string text = BoomboxAuraCodec.ReadHeader(package, out num); if (receivedSession == null || (!(receivedSession != text) && num > receivedRevision)) { BoomboxAuraBalance boomboxAuraBalance = BoomboxAuraCodec.ReadBalance(package); List<BoomboxAuraSource> sources = BoomboxAuraCodec.ReadSources(package); balance = boomboxAuraBalance; snapshot = sources; receivedSession = text; receivedRevision = num; lastSnapshot = Time.realtimeSinceStartup; BoomboxMusicNetwork.AuraStateReceived(sources); } } catch (Exception ex) { BoomboxPlugin.Instance?.LogError("Boombox aura state rejected: " + ex.Message); } } private void Update() { if ((Object)(object)network == (Object)null || BoomboxPlugin.Instance?.Settings == null) { return; } List<ZNetPeer> peers = network.GetPeers(); foreach (ZNetPeer item in peers) { Connected(item); } HashSet<ZRpc> rpcs = new HashSet<ZRpc>(peers.Select((ZNetPeer p) => p.m_rpc)); registered.RemoveWhere((ZRpc rpc) => !rpcs.Contains(rpc)); ZRpc[] array = acceptedSessions.Keys.Where((ZRpc r) => !rpcs.Contains(r)).ToArray(); foreach (ZRpc key in array) { acceptedSessions.Remove(key); } if (!network.IsServer() && !peers.Any((ZNetPeer p) => p.m_server)) { snapshot.Clear(); balance = null; receivedSession = null; receivedRevision = 0L; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (!(realtimeSinceStartup < next)) { next = realtimeSinceStartup + 0.25f; revision++; if (network.IsServer()) { TickServer(peers, realtimeSinceStartup); } else { TickClient(peers); } } } private void TickClient(List<ZNetPeer> peers) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown ZNetPeer val = ((IEnumerable<ZNetPeer>)peers).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer p) => p.m_server && p.IsReady() && BoomboxNetworkHandshake.Attach(network).Accepts(p.m_rpc))); if (val != null && !((Object)(object)Player.m_localPlayer == (Object)null) && balance != null) { ZPackage val2 = new ZPackage(); BoomboxAuraCodec.Header(val2, session, revision); BoomboxAuraCodec.Sources(val2, BoomboxAuraSources.Local()); val.m_rpc.Invoke("BoomboxAuraSources", new object[1] { val2 }); } } private void TickServer(List<ZNetPeer> peers, float now) { //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Expected O, but got Unknown HashSet<long> hashSet = new HashSet<long>(from p in peers where p.IsReady() && BoomboxNetworkHandshake.Attach(network).Accepts(p.m_rpc) select p.m_uid); Player local = Player.m_localPlayer; if ((Object)(object)local != (Object)null) { long uid = ZNet.GetUID(); hashSet.Add(uid); ZNetView component = ((Component)local).GetComponent<ZNetView>(); ZDO character = ((component != null) ? component.GetZDO() : null); List<BoomboxAuraSource> list = BoomboxAuraSources.Local(); if (character != null && list.All((BoomboxAuraSource s) => BoomboxAuraSources.ValidateAndNormalize(s, uid, local.GetPlayerID(), local.GetPlayerName(), character)) && registry.Replace(uid, session, revision, list, now)) { BoomboxMusicNetwork.SourcesReceived(uid, list); } } registry.Prune(hashSet, now); balance = BoomboxPartySettings.Capture(BoomboxPlugin.Instance.Settings); ZPackage val = new ZPackage(); BoomboxAuraCodec.Header(val, session, revision); BoomboxAuraCodec.Balance(val, balance); BoomboxAuraCodec.Sources(val, registry.Sources.ToList()); foreach (ZNetPeer peer in peers) { if (hashSet.Contains(peer.m_uid)) { peer.m_rpc.Invoke("BoomboxAuraState", new object[1] { val }); } } } private void OnDestroy() { foreach (ZRpc item in registered) { item.Unregister("BoomboxAuraSources"); item.Unregister("BoomboxAuraState"); } registered.Clear(); acceptedSessions.Clear(); snapshot.Clear(); balance = null; if ((Object)(object)Current == (Object)(object)this) { Current = null; BoomboxAuraRecipient.Clear(); } } } public static class BoomboxAuraRecipient { private static Player recipient; private static int appliedFrame = -1; private static readonly double[] elapsed = new double[3]; private static readonly double[] ticks = new double[3]; public static double HealthTicks => ticks[0]; public static double StaminaTicks => ticks[1]; public static double EitrTicks => ticks[2]; internal static void Apply(Player player) { if ((Object)(object)player != (Object)(object)Player.m_localPlayer || Time.frameCount == appliedFrame) { return; } if ((Object)(object)recipient != (Object)(object)player) { Clear(); recipient = player; } appliedFrame = Time.frameCount; BoomboxAuraBalance balance = BoomboxAuraNetwork.Balance; BoomboxAuraAggregate boomboxAuraAggregate = BoomboxAuraNetwork.For(player); if (((Character)player).IsDead() || ((Character)player).IsTeleporting() || balance == null || Time.timeScale <= 0f || AudioListener.pause) { Array.Clear(elapsed, 0, elapsed.Length); BoomboxAuraHud.Remove(player); return; } BoomboxAuraHud.Present(player, boomboxAuraAggregate, balance); for (int i = 0; i < 3; i++) { float num = boomboxAuraAggregate.Values[(int)BoomboxAuraMath.RecoveryBonus(i)]; if (num <= 0f) { elapsed[i] = 0.0; continue; } elapsed[i] += Time.unscaledDeltaTime; double num2 = Math.Floor(elapsed[i] / (double)balance.Period[i]); elapsed[i] -= num2 * (double)balance.Period[i]; if (num2 <= 0.0) { continue; } ticks[i] += num2; switch (i) { case 0: ((Character)player).Heal(Grant(num, num2, ((Character)player).GetMaxHealth() - ((Character)player).GetHealth()), false); continue; case 1: ((Character)player).AddStamina(Grant(num, num2, ((Character)player).GetMaxStamina() - player.GetStamina())); continue; } if (((Character)player).GetMaxEitr() > 0f) { ((Character)player).AddEitr(Grant(num, num2, ((Character)player).GetMaxEitr() - player.GetEitr())); } } } private static float Grant(float amount, double count, float missing) { return (float)Math.Min(Math.Max(0f, missing), (double)amount * count); } public static void Clear() { BoomboxAuraHud.Clear(recipient); recipient = null; appliedFrame = -1; Array.Clear(elapsed, 0, elapsed.Length); Array.Clear(ticks, 0, ticks.Length); } } internal sealed class BoomboxAuraRegistry { private sealed class Batch { internal string Session; internal long Revision; } private sealed class Entry { internal BoomboxAuraSource Source; internal float Heard; } private readonly Dictionary<long, Batch> batches = new Dictionary<long, Batch>(); private readonly Dictionary<string, Entry> sources = new Dictionary<string, Entry>(StringComparer.Ordinal); internal IReadOnlyList<BoomboxAuraSource> Sources => sources.Values.Select((Entry e) => e.Source).ToArray(); internal bool Replace(long peer, string session, long revision, List<BoomboxAuraSource> incoming, float now) { //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) if (batches.TryGetValue(peer, out var value) && (value.Session != session || revision <= value.Revision)) { return false; } foreach (BoomboxAuraSource item in incoming) { if (sources.TryGetValue(item.Id, out var value2) && value2.Source.Peer != peer) { object obj; if (!((ZDOID)(ref item.WorldItem)).IsNone()) { ZDOMan instance = ZDOMan.instance; obj = ((instance != null) ? instance.GetZDO(item.WorldItem) : null); } else { obj = null; } ZDO val = (ZDO)obj; bool num = val != null && val.GetOwner() == peer && value2.Source.WorldItem == item.WorldItem; int num2; if (item.Worn && !((ZDOID)(ref value2.Source.WorldItem)).IsNone()) { ZDOMan instance2 = ZDOMan.instance; num2 = ((((instance2 != null) ? instance2.GetZDO(value2.Source.WorldItem) : null) == null) ? 1 : 0); } else { num2 = 0; } bool flag = (byte)num2 != 0; if (!num && !flag) { return false; } } } HashSet<string> ids = new HashSet<string>(incoming.Select((BoomboxAuraSource s) => s.Id), StringComparer.Ordinal); string[] array = (from p in sources where p.Value.Source.Peer == peer && !ids.Contains(p.Key) select p.Key).ToArray(); foreach (string key in array) { sources.Remove(key); } foreach (BoomboxAuraSource item2 in incoming) { sources[item2.Id] = new Entry { Source = item2, Heard = now }; } batches[peer] = new Batch { Session = session, Revision = revision }; return true; } internal void Prune(HashSet<long> connected, float now) { string[] array = (from p in sources where !connected.Contains(p.Value.Source.Peer) || now - p.Value.Heard > 1.25f select p.Key).ToArray(); foreach (string key in array) { sources.Remove(key); } long[] array2 = batches.Keys.Where((long p) => !connected.Contains(p)).ToArray(); foreach (long key2 in array2) { batches.Remove(key2); } } } internal static class BoomboxAuraSources { internal static readonly string[] Properties = (from index in Enumerable.Range(0, 5) select BoomboxAuraMath.CassetteProperty((BoomboxDecorationBonus)index)).ToArray(); internal static List<BoomboxAuraSource> Local() { //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0165: 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_016a: Unknown result type (might be due to invalid IL or missing references) List<BoomboxAuraSource> list = new List<BoomboxAuraSource>(); BoomboxPlugin instance = BoomboxPlugin.Instance; Player localPlayer = Player.m_localPlayer; if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null || !instance.AudioWorldActive) { return list; } foreach (AudioDeck value in instance.Decks.Values) { if ((Object)(object)value == (Object)null || value.State.owner != localPlayer.GetPlayerID() || value.Item == null) { continue; } DeckState state = value.State; BoomboxAuraSource obj = new BoomboxAuraSource { Id = state.id, Owner = state.owner, OwnerName = localPlayer.GetPlayerName(), Worn = state.onBack, Active = (!((Character)localPlayer).IsDead() && !((Character)localPlayer).IsTeleporting() && !value.StoragePaused && (value.IsActivePlayback || value.IsSyntheticActive)), Position = ((Component)value).transform.position }; ItemDrop worldDrop = value.WorldDrop; ZDOID? obj2; if (worldDrop == null) { obj2 = null; } else { ZNetView component = ((Component)worldDrop).GetComponent<ZNetView>(); obj2 = ((component == null) ? ((ZDOID?)null) : component.GetZDO()?.m_uid); } obj.WorldItem = (ZDOID)(((??)obj2) ?? ZDOID.None); BoomboxAuraSource boomboxAuraSource = obj; boomboxAuraSource.Active &= instance.Settings.LocalGameplayEnabled; for (int i = 0; i < 5; i++) { boomboxAuraSource.Ranks[i] = state.insertedCassette?.Rank(Properties[i]) ?? 0; } HashSet<int> hashSet = new HashSet<int>(); foreach (BoomboxDecorationPlacement decorationPlacement in state.decorationPlacements) { if (decorationPlacement != null && hashSet.Add(decorationPlacement.slot) && state.unlockedDecorationSlots.Contains(decorationPlacement.slot) && state.unlockedDecorationPrefabs.Contains(decorationPlacement.prefab) && BoomboxDecorationCatalog.DataEligible(decorationPlacement.prefab, decorationPlacement.slot)) { boomboxAuraSource.Decorations.Add(new BoomboxAuraDecoration { Slot = decorationPlacement.slot, Prefab = decorationPlacement.prefab, Effect = (int)BoomboxDecorationCatalog.DataEffect(decorationPlacement.prefab, decorationPlacement.effect, BoomboxAuraNetwork.Balance?.Fallback ?? instance.Settings.DecorationModFallback.Value) }); } } list.Add(boomboxAuraSource); } return list; } internal static bool Identity(ZNetPeer peer, out ZDO character, out long player, out string name) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) object obj; if (peer == null || !peer.IsReady()) { obj = null; } else { ZDOMan instance = ZDOMan.instance; obj = ((instance != null) ? instance.GetZDO(peer.m_characterID) : null); } character = (ZDO)obj; ZDO obj2 = character; player = ((obj2 != null) ? obj2.GetLong(ZDOVars.s_playerID, 0L) : 0); ZDO obj3 = character; name = ((obj3 != null) ? obj3.GetString(ZDOVars.s_playerName, "") : null) ?? ""; if (player != 0L && character != null) { return character.GetOwner() == peer.m_uid; } return false; } internal static bool ValidateAndNormalize(BoomboxAuraSource source, long peer, long player, string name, ZDO character) { //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Expected O, but got Unknown //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) if (source.Owner != player || source.Id == null || character == null || source.Decorations.Any((BoomboxAuraDecoration d) => !BoomboxDecorationCatalog.DataEligible(d.Prefab, d.Slot))) { return false; } source.Peer = peer; source.OwnerName = ((name.Length > 128) ? name.Substring(0, 128) : name); source.Active &= !character.GetBool(ZDOVars.s_dead, false); if (source.Worn) { if (!((ZDOID)(ref source.WorldItem)).IsNone()) { return false; } return Vector3.Distance(source.Position, character.GetPosition()) <= 5f; } if (((ZDOID)(ref source.WorldItem)).IsNone()) { return !source.Active; } ZDO zDO = ZDOMan.instance.GetZDO(source.WorldItem); if (zDO == null || zDO.GetPrefab() != StringExtensionMethods.GetStableHashCode("ValheimBoombox_Item")) { return false; } ItemData val = new ItemData(); ZNetScene instance = ZNetScene.instance; val.m_dropPrefab = ((instance != null) ? instance.GetPrefab("ValheimBoombox_Item") : null); ItemData val2 = val; ItemDrop.LoadFromZDO(val2, zDO, -1); DeckState saved = BoomboxItemData.Read(val2).deck; if (saved.id != source.Id || (saved.owner != player && zDO.GetOwner() != peer)) { return false; } source.Position = zDO.GetPosition(); for (int num = 0; num < 5; num++) { if (source.Ranks[num] != (saved.insertedCassette?.Rank(Properties[num]) ?? 0)) { return false; } } BoomboxDecorationPlacement[] placements = saved.decorationPlacements.Where((BoomboxDecorationPlacement p) => p != null && saved.unlockedDecorationSlots.Contains(p.slot) && saved.unlockedDecorationPrefabs.Contains(p.prefab) && BoomboxDecorationCatalog.DataEligible(p.prefab, p.slot)).ToArray(); if (placements.Length == source.Decorations.Count) { return source.Decorations.All((BoomboxAuraDecoration d) => placements.Any((BoomboxDecorationPlacement p) => p.slot == d.Slot && p.prefab == d.Prefab && BoomboxDecorationCatalog.DataEffect(p.prefab, p.effect, BoomboxAuraNetwork.Balance.Fallback) == (BoomboxDecorationBonus)d.Effect)); } return false; } } public sealed class BoomboxAuraStatus : SE_Stats { public int BonusIndex; public float BonusValue; public float BonusPeriod; public int BonusSources; public override string GetTooltipString() { return ((StatusEffect)this).m_tooltip; } } internal static class BoomboxAuraHud { private static readonly BoomboxAuraStatus[] templates = new BoomboxAuraStatus[5]; private static readonly string[] colors = new string[5] { "Red", "Green", "Yellow", "Blue", "Purple" }; internal static string Name(int index) { return BoomboxText.Get((new string[5] { "text.wave_damage", "text.health", "text.stamina", "text.eitr", "text.speed" })[index]); } internal static string Number(float value, int index) { return (value * ((index == 0 || index == 4) ? 100f : 1f)).ToString("0.##", BoomboxText.Culture) + ((index == 0 || index == 4) ? " %" : ""); } internal static void Present(Player player, BoomboxAuraAggregate aggregate, BoomboxAuraBalance balance) { SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan == null) { return; } int index; for (index = 0; index < 5; index++) { int stableHashCode = StringExtensionMethods.GetStableHashCode(Key(index)); if (aggregate.Values[index] <= 0f) { sEMan.RemoveStatusEffect(stableHashCode, true); continue; } BoomboxAuraStatus boomboxAuraStatus = sEMan.GetStatusEffect(stableHashCode) as BoomboxAuraStatus; if ((Object)(object)boomboxAuraStatus == (Object)null) { if ((Object)(object)templates[index] == (Object)null) { templates[index] = ScriptableObject.CreateInstance<BoomboxAuraStatus>(); ((Object)templates[index]).name = Key(index); } ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab("ValheimBoombox_Cassette" + colors[index]) : null); BoomboxAuraStatus obj = templates[index]; object icon; if (!VikingBoomboxVisual.CanRender) { if (val == null) { icon = null; } else { ItemDrop component = val.GetComponent<ItemDrop>(); icon = ((component != null) ? component.m_itemData.GetIcon() : null); } } else { icon = BoomboxBonusLook.Icon(index); } ((StatusEffect)obj).m_icon = (Sprite)icon; boomboxAuraStatus = sEMan.AddStatusEffect((StatusEffect)(object)templates[index], false, 0, 0f, (short)0) as BoomboxAuraStatus; } if ((Object)(object)boomboxAuraStatus == (Object)null) { continue; } boomboxAuraStatus.BonusIndex = index; boomboxAuraStatus.BonusValue = aggregate.Values[index]; boomboxAuraStatus.BonusSources = aggregate.Contributions.Count((BoomboxAuraContribution c) => c.Values[index] > 0f); boomboxAuraStatus.BonusPeriod = ((index > 0 && index < 4) ? balance.Period[index - 1] : 0f); ((StatusEffect)boomboxAuraStatus).m_name = Name(index); string text = ((index > 0 && index < 4) ? (BoomboxText.Get("text.every_2") + balance.Period[index - 1].ToString("0.##", BoomboxText.Culture) + BoomboxText.Get("text.s")) : ""); ((StatusEffect)boomboxAuraStatus).m_tooltip = Name(index) + ": +" + Number(aggregate.Values[index], index) + text; BoomboxAuraStatus boomboxAuraStatus2 = boomboxAuraStatus; ((StatusEffect)boomboxAuraStatus2).m_tooltip = ((StatusEffect)boomboxAuraStatus2).m_tooltip + BoomboxText.Get("text.source_boomboxes") + boomboxAuraStatus.BonusSources; foreach (IGrouping<long, BoomboxAuraContribution> item in from c in aggregate.Contributions where c.Values[index] > 0f group c by c.Owner) { BoomboxAuraStatus boomboxAuraStatus3 = boomboxAuraStatus; ((StatusEffect)boomboxAuraStatus3).m_tooltip = ((StatusEffect)boomboxAuraStatus3).m_tooltip + "\n" + Clean(item.First().OwnerName) + ": +" + Number(item.Sum((BoomboxAuraContribution c) => c.Values[index]), index); } ((SE_Stats)boomboxAuraStatus).m_speedModifier = ((index == 4) ? aggregate.Values[index] : 0f); } } internal static void Remove(Player player) { if (!((Object)(object)player != (Object)null)) { return; } for (int i = 0; i < 5; i++) { SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan != null) { sEMan.RemoveStatusEffect(StringExtensionMethods.GetStableHashCode(Key(i)), true); } } } internal static void Clear(Player player) { Remove(player); for (int i = 0; i < 5; i++) { if ((Object)(object)templates[i] != (Object)null) { Object.Destroy((Object)(object)templates[i]); } templates[i] = null; } } private static string Key(int index) { return "ValheimBoombox_Shared_" + colors[index]; } private static string Clean(string text) { if (!string.IsNullOrWhiteSpace(text)) { return text.Replace("<", "").Replace(">", "").Replace("\n", " ") .Replace("\r", " "); } return BoomboxText.Get("text.player"); } } internal static class BoomboxBoatDrag { private static readonly FieldRef<Ship, float> LastDepth = AccessTools.FieldRefAccess<Ship, float>("m_lastDepth"); private static readonly float[] losses = new float[25]; private static Ship ship; private static Rigidbody body; private static int count; private static int next; internal static void Step(Ship current) { //IL_0056: 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) if ((Object)(object)current != (Object)(object)ship) { ship = current; body = (((Object)(object)current == (Object)null) ? null : ((Component)current).GetComponent<Rigidbody>()); count = 0; next = 0; } if (!((Object)(object)ship == (Object)null) && !((Object)(object)body == (Object)null)) { float num = Vector3.Dot(body.linearVelocity, ((Component)ship).transform.forward); float num2 = Mathf.Clamp01(Mathf.Abs(LastDepth.Invoke(ship)) / ship.m_forceDistance); losses[next] = ship.m_dampingForward * num * num * num2; next = (next + 1) % losses.Length; count = Mathf.Min(count + 1, losses.Length); } } internal static float Take(Ship current) { if ((Object)(object)current != (Object)(object)ship) { return 0f; } float num = 0f; for (int i = 1; i <= count; i++) { num += losses[(next - i + losses.Length) % losses.Length]; } count = 0; return num; } } [DefaultExecutionOrder(10001)] public sealed class BoomboxBoatHelp : MonoBehaviour { internal const float DragShare = 0.203f; internal const int MaximumSteps = 25; internal const float MovingSpeed = 0.5f; private AudioDeck deck; private BoomboxBoatWave wave; private bool waveFailed; private int beats; internal void Initialize(AudioDeck owner) { deck = owner; beats = owner.EmittedWaves; } internal static bool TryPush(Ship ship) { //IL_000a: 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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) if ((Object)(object)ship == (Object)null || (int)ship.GetSpeedSetting() == 0) { return false; } ZNetView component = ((Component)ship).GetComponent<ZNetView>(); Rigidbody component2 = ((Component)ship).GetComponent<Rigidbody>(); if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner() || (Object)(object)component2 == (Object)null) { return false; } Vector3 linearVelocity = component2.linearVelocity; Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(linearVelocity.x, 0f, linearVelocity.z); if (((Vector3)(ref val)).magnitude < 0.5f) { return false; } component2.AddForce(((Vector3)(ref val)).normalized * (0.203f * BoomboxBoatDrag.Take(ship)), (ForceMode)2); return true; } private void LateUpdate() { if ((Object)(object)deck == (Object)null || deck.State == null) { return; } bool flag = deck.EmittedWaves != beats; beats = deck.EmittedWaves; bool worn; Ship val = HelpedShip(out worn); if ((Object)(object)val == (Object)null) { wave?.Clear(); return; } bool pushed = flag && TryPush(val); if (!BoomboxPlugin.Instance.Settings.ShowVisualEffects.Value) { wave?.Clear(); } else { if (waveFailed || (Object)(object)deck.Visual == (Object)null) { return; } try { if (wave == null) { wave = new BoomboxBoatWave(); } wave.Present(deck, val, pushed); } catch (Exception ex) { waveFailed = true; wave?.Destroy(); wave = null; BoomboxPlugin.Instance?.LogError(BoomboxText.Get("text.boombox_sail_wave_is_not_shown_boat_impulses_still_work") + ex.Message); } } } private void FixedUpdate() { bool worn; Ship current = HelpedShip(out worn); if (worn) { BoomboxBoatDrag.Step(current); } } private Ship HelpedShip(out bool worn) { Player localPlayer = Player.m_localPlayer; BoomboxSettings boomboxSettings = BoomboxPlugin.Instance?.Settings; worn = (Object)(object)deck != (Object)null && deck.State != null && (Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead() && deck.Item != null && deck.State.onBack && deck.Item == BoomboxEquipment.