using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text.Json;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using DiceKingdoms.Game;
using DiceKingdoms.Interface;
using DiceKingdoms.Interface.Particles;
using DiceKingdoms.Meta;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.Attributes;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("DiceKingdoms.VillageAudioOverhaul")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0")]
[assembly: AssemblyProduct("DiceKingdoms.VillageAudioOverhaul")]
[assembly: AssemblyTitle("DiceKingdoms.VillageAudioOverhaul")]
[assembly: AssemblyVersion("1.1.0.0")]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
}
namespace DiceKingdoms.VillageAudioOverhaul
{
public sealed class AudioFileHandler : DownloadHandler
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate IntPtr CreateCall(IntPtr self, IntPtr url, AudioType type);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate IntPtr ClipCall(IntPtr self);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void StreamCall(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value);
private static readonly CreateCall CreateAudio = IL2CPP.ResolveICall<CreateCall>("UnityEngine.Networking.DownloadHandlerAudioClip::Create");
private static readonly ClipCall GetAudio = IL2CPP.ResolveICall<ClipCall>("UnityEngine.Networking.DownloadHandlerAudioClip::get_audioClip");
private static readonly StreamCall SetStream = IL2CPP.ResolveICall<StreamCall>("UnityEngine.Networking.DownloadHandlerAudioClip::set_streamAudio");
public AudioFileHandler(IntPtr pointer)
: base(pointer)
{
}
public AudioFileHandler()
: base(ClassInjector.DerivedConstructorPointer<AudioFileHandler>())
{
ClassInjector.DerivedConstructorBody((Il2CppObjectBase)(object)this);
}
[HideFromIl2Cpp]
internal void Initialize(string uri, AudioType type, bool streaming)
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
((DownloadHandler)this).m_Ptr = CreateAudio(((Il2CppObjectBase)this).Pointer, IL2CPP.ManagedStringToIl2Cpp(uri), type);
if (((DownloadHandler)this).m_Ptr == IntPtr.Zero)
{
throw new IOException("Unity could not create its audio download handler");
}
SetStream(((Il2CppObjectBase)this).Pointer, streaming);
}
[HideFromIl2Cpp]
internal AudioClip? GetClip()
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Expected O, but got Unknown
IntPtr intPtr = GetAudio(((Il2CppObjectBase)this).Pointer);
if (!(intPtr == IntPtr.Zero))
{
return new AudioClip(intPtr);
}
return null;
}
}
public sealed class AudioMap
{
public Dictionary<string, string[]> Music { get; set; } = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
public Dictionary<string, string[]> Sfx { get; set; } = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
public Dictionary<string, string> ClipEvents { get; set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public Dictionary<string, string> ResourceEvents { get; set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public Dictionary<string, string> BuildingEvents { get; set; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public static AudioMap Read(string path)
{
AudioMap audioMap = (File.Exists(path) ? (JsonSerializer.Deserialize<AudioMap>(File.ReadAllText(path), new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
ReadCommentHandling = JsonCommentHandling.Skip
}) ?? throw new InvalidDataException("Empty audio map")) : new AudioMap());
if (audioMap.Music == null || audioMap.Sfx == null || audioMap.ClipEvents == null || audioMap.ResourceEvents == null || audioMap.BuildingEvents == null)
{
throw new InvalidDataException("Map sections cannot be null");
}
audioMap.Music = new Dictionary<string, string[]>(audioMap.Music, StringComparer.OrdinalIgnoreCase);
audioMap.Sfx = new Dictionary<string, string[]>(audioMap.Sfx, StringComparer.OrdinalIgnoreCase);
audioMap.ClipEvents = new Dictionary<string, string>(audioMap.ClipEvents, StringComparer.OrdinalIgnoreCase);
audioMap.ResourceEvents = new Dictionary<string, string>(audioMap.ResourceEvents, StringComparer.OrdinalIgnoreCase);
audioMap.BuildingEvents = new Dictionary<string, string>(audioMap.BuildingEvents, StringComparer.OrdinalIgnoreCase);
return audioMap;
}
public static string? SafeFile(string root, string relative)
{
if (string.IsNullOrWhiteSpace(relative) || Path.IsPathRooted(relative))
{
return null;
}
string text = Path.GetFullPath(root).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
string fullPath = Path.GetFullPath(Path.Combine(text, relative));
if (!fullPath.StartsWith(text, StringComparison.OrdinalIgnoreCase))
{
return null;
}
FileSystemInfo fileSystemInfo = new FileInfo(fullPath);
while (fileSystemInfo != null && fileSystemInfo.FullName.Length >= text.Length)
{
if (fileSystemInfo.Exists && (fileSystemInfo.Attributes & FileAttributes.ReparsePoint) != FileAttributes.None)
{
return null;
}
fileSystemInfo = ((fileSystemInfo is FileInfo fileInfo) ? fileInfo.Directory : ((DirectoryInfo)fileSystemInfo).Parent);
}
if (!Supported(fullPath))
{
return null;
}
return fullPath;
}
public static bool Supported(string path)
{
string text = Path.GetExtension(path).ToLowerInvariant();
return text == ".wav" || text == ".ogg" || text == ".mp3";
}
public static IEnumerable<string> Files(string root, string key, Dictionary<string, string[]> entries)
{
if (entries.TryGetValue(key, out string[] value))
{
return (from p in value ?? Array.Empty<string>()
select SafeFile(root, p) into p
where p != null && File.Exists(p)
select p).Cast<string>().Distinct<string>(StringComparer.OrdinalIgnoreCase);
}
List<string> list = new List<string>();
string[] array = new string[3] { ".ogg", ".wav", ".mp3" };
foreach (string text in array)
{
string text2 = SafeFile(root, key + text);
if (text2 != null && File.Exists(text2))
{
list.Add(text2);
}
}
string text3 = SafeFile(root, key + "/placeholder.ogg");
if (text3 != null && Directory.Exists(Path.GetDirectoryName(text3)))
{
list.AddRange((from p in Directory.EnumerateFiles(Path.GetDirectoryName(text3)).Where(Supported)
select SafeFile(root, Path.GetRelativePath(root, p)) into p
where p != null
select p).Cast<string>());
}
return list.Distinct<string>(StringComparer.OrdinalIgnoreCase).OrderBy<string, string>((string p) => p, StringComparer.OrdinalIgnoreCase);
}
}
public sealed class ShuffleBag
{
private readonly Random random;
private readonly Queue<int> remaining = new Queue<int>();
private int last = -1;
private int size = -1;
public ShuffleBag(int? seed = null)
{
random = (seed.HasValue ? new Random(seed.Value) : new Random());
}
public int Next(int count)
{
if (count < 1)
{
throw new ArgumentOutOfRangeException("count");
}
if (size != count)
{
remaining.Clear();
size = count;
last = -1;
}
if (remaining.Count == 0)
{
int[] array = (from _ in Enumerable.Range(0, count)
orderby random.Next()
select _).ToArray();
if (count > 1 && array[0] == last)
{
ref int reference = ref array[0];
ref int reference2 = ref array[1];
int num = array[1];
int num2 = array[0];
reference = num;
reference2 = num2;
}
int[] array2 = array;
foreach (int item in array2)
{
remaining.Enqueue(item);
}
}
return last = remaining.Dequeue();
}
}
public sealed class AudioRuntime : MonoBehaviour
{
private sealed class Snapshot
{
internal Sound Sound;
internal AudioClip[] Clips = Array.Empty<AudioClip>();
internal float Min;
internal float Max;
internal float MinPitch;
internal float MaxPitch;
internal bool Loop;
internal bool Changed;
internal string Event = "";
internal bool FieldAssigned;
internal bool Ambiguous;
internal void Restore()
{
if (Object.op_Implicit((Object)(object)Sound) && Changed)
{
Sound.clips = new Il2CppReferenceArray<AudioClip>(Clips);
Sound.minVolume = Min;
Sound.maxVolume = Max;
Sound.minPitch = MinPitch;
Sound.maxPitch = MaxPitch;
Sound.loop = Loop;
Changed = false;
}
}
}
private sealed class Selection
{
internal Music Music;
internal float OriginalDuration;
internal string Context = "";
internal Sound? Selected;
internal readonly Dictionary<string, Sound> Sounds = new Dictionary<string, Sound>();
internal bool JingleDone;
internal bool HasPlayed;
}
private sealed record Plan(AudioMap Map, Dictionary<string, string[]> Music, Dictionary<string, string[]> Sfx);
private Task<Plan>? planning;
private Plan? plan;
private readonly Dictionary<string, AudioClip> clips = new Dictionary<string, AudioClip>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, AudioClip[]> music = new Dictionary<string, AudioClip[]>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, AudioClip[]> sfx = new Dictionary<string, AudioClip[]>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, ShuffleBag> bags = new Dictionary<string, ShuffleBag>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<int, Snapshot> snapshots = new Dictionary<int, Snapshot>();
private readonly Dictionary<int, Selection> selections = new Dictionary<int, Selection>();
private readonly HashSet<int> ownedSounds = new HashSet<int>();
private readonly HashSet<int> seen = new HashSet<int>();
private readonly List<Sound> created = new List<Sound>();
private readonly List<AudioSources> pools = new List<AudioSources>();
private readonly Dictionary<int, AudioSource> muted = new Dictionary<int, AudioSource>();
private Queue<string> pending = new Queue<string>();
private HashSet<string> streamingPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private UnityWebRequest? request;
private AudioFileHandler? handler;
private string loading = "";
private bool loadingStream;
private long decodedBytes;
private bool stopped;
private int scene = int.MinValue;
private int scans;
private float nextScan;
private MainMenu? activeMenu;
private AudioSources? menuPool;
private PooledSource? menuSlot;
private long menuTicket;
private Snapshot? menuSnapshot;
private readonly Dictionary<string, Sound> effectSounds = new Dictionary<string, Sound>();
private readonly HashSet<int> effectSoundIds = new HashSet<int>();
private readonly HashSet<IntPtr> effectClipPointers = new HashSet<IntPtr>();
private readonly Dictionary<string, float> resourcePlayedAt = new Dictionary<string, float>();
private readonly Dictionary<int, (BuildingVisual Visual, float Health)> buildingHealth = new Dictionary<int, (BuildingVisual, float)>();
private Resource? resourceContext;
private string? buildingContext;
private string? unitContext;
private int resourceFrame = -1;
private int buildingFrame = -1;
private int unitFrame = -1;
public bool Ready { get; private set; }
public int LoadedClipCount => clips.Count;
public int ReplacementSoundCount => snapshots.Values.Count((Snapshot s) => s.Changed);
public string LastContext { get; private set; } = "";
public IntPtr LastStockTrackPointer { get; private set; }
public string LastEffectEvent { get; private set; } = "";
public string LastEffectClip { get; private set; } = "";
public int SuppressedResourceRepeats { get; private set; }
public AudioRuntime(IntPtr pointer)
: base(pointer)
{
}
internal bool OwnsSound(Sound? sound)
{
if ((Object)(object)sound != (Object)null && Object.op_Implicit((Object)(object)sound))
{
return ownedSounds.Contains(((Object)sound).GetInstanceID());
}
return false;
}
internal float OriginalDuration(Music item)
{
if (!selections.TryGetValue(((Object)item).GetInstanceID(), out Selection value))
{
return item.transitionDuration;
}
return value.OriginalDuration;
}
public void Start()
{
string root = Path.Combine(Plugin.Folder, "Audio");
planning = Task.Run(delegate
{
AudioMap audioMap = AudioMap.Read(Path.Combine(Plugin.Folder, "audio-map.json"));
Dictionary<string, string[]> dictionary = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
Dictionary<string, string[]> dictionary2 = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
foreach (string item in new string[10] { "menu", "lobby", "start", "harvest", "disaster", "building", "planning", "combat", "victory", "defeat" }.Concat(audioMap.Music.Keys).Distinct())
{
dictionary[item] = AudioMap.Files(root, item, audioMap.Music).Take(32).ToArray();
}
string[] second = new string[8] { "ui_click", "ui_hover", "building_place", "building_rotate", "dice_move", "dice_skull", "harvest_resource", "dice_pop" };
foreach (string item2 in audioMap.ClipEvents.Values.Where((string v) => !v.StartsWith("music:")).Concat(audioMap.Sfx.Keys).Concat(second)
.Distinct())
{
dictionary2[item2] = AudioMap.Files(root, item2, audioMap.Sfx).Take(32).ToArray();
}
return new Plan(audioMap, dictionary, dictionary2);
});
}
public void Update()
{
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
if (stopped)
{
return;
}
try
{
if (!Ready)
{
LoadNext();
}
UpdateMenuPlaylist();
if (Plugin.Dirty)
{
Plugin.Dirty = false;
RefreshSettings();
}
Scene activeScene = SceneManager.GetActiveScene();
int handle = ((Scene)(ref activeScene)).handle;
if (handle != scene)
{
scene = handle;
scans = 0;
nextScan = Time.realtimeSinceStartup + 0.5f;
Prune();
}
if (Ready && scans < 3 && Time.realtimeSinceStartup >= nextScan)
{
Scan();
scans++;
nextScan = Time.realtimeSinceStartup + 2f;
}
}
catch (Exception ex)
{
Plugin.Instance.Error(ex);
}
}
private void LoadNext()
{
//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
//IL_0127: Unknown result type (might be due to invalid IL or missing references)
//IL_012d: Invalid comparison between Unknown and I4
//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
//IL_0301: Unknown result type (might be due to invalid IL or missing references)
//IL_02f7: Unknown result type (might be due to invalid IL or missing references)
//IL_033f: Unknown result type (might be due to invalid IL or missing references)
//IL_035b: Unknown result type (might be due to invalid IL or missing references)
//IL_0365: Expected O, but got Unknown
//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
//IL_04ab: Unknown result type (might be due to invalid IL or missing references)
//IL_04b2: Expected O, but got Unknown
if (plan == null)
{
if (planning == null || !planning.IsCompleted)
{
return;
}
try
{
plan = planning.GetAwaiter().GetResult();
}
catch (Exception ex)
{
Plugin.Instance.Error(ex);
Ready = true;
return;
}
pending = new Queue<string>(plan.Music.Values.Concat<string[]>(plan.Sfx.Values).SelectMany((string[] x) => x).Distinct<string>(StringComparer.OrdinalIgnoreCase)
.Take(256));
streamingPaths = new HashSet<string>(plan.Music.Values.SelectMany((string[] paths) => paths), StringComparer.OrdinalIgnoreCase);
}
if (request != null)
{
if (!request.isDone)
{
return;
}
try
{
if ((int)request.result != 1)
{
throw new IOException(Path.GetFileName(loading) + ": " + request.error);
}
AudioClip clip = handler.GetClip();
if ((Object)(object)clip == (Object)null || !Object.op_Implicit((Object)(object)clip) || clip.samples <= 0 || clip.channels <= 0 || clip.length <= 0.01f)
{
if (Object.op_Implicit((Object)(object)clip))
{
Object.Destroy((Object)(object)clip);
}
throw new InvalidDataException("Invalid audio: " + Path.GetFileName(loading));
}
long num = (loadingStream ? (new FileInfo(loading).Length + 2L * (long)clip.frequency * clip.channels * 4) : ((long)clip.samples * (long)clip.channels * 4));
if (decodedBytes + num > 268435456)
{
Object.Destroy((Object)(object)clip);
throw new InvalidDataException("256 MiB decoded audio budget reached: " + Path.GetFileName(loading));
}
decodedBytes += num;
((Object)clip).name = "VillageAudio/" + Path.GetFileName(loading);
((Object)clip).hideFlags = (HideFlags)61;
clips.Add(loading, clip);
}
catch (Exception ex2)
{
Plugin.Instance.Error(ex2);
}
finally
{
request.Dispose();
request = null;
handler = null;
}
}
if (pending.Count > 0)
{
loading = pending.Dequeue();
string text = Path.GetExtension(loading).ToLowerInvariant();
AudioType val = ((text == ".wav") ? ((AudioType)20) : ((!(text == ".ogg")) ? ((AudioType)13) : ((AudioType)14)));
AudioType type = val;
loadingStream = streamingPaths.Contains(loading);
handler = new AudioFileHandler();
try
{
string absoluteUri = new Uri(loading).AbsoluteUri;
handler.Initialize(absoluteUri, type, loadingStream);
request = new UnityWebRequest(absoluteUri, "GET", (DownloadHandler)(object)handler, (UploadHandler)null);
}
catch
{
((DownloadHandler)handler).Dispose();
handler = null;
throw;
}
request.timeout = 20;
request.SendWebRequest();
return;
}
foreach (KeyValuePair<string, string[]> item in plan.Music)
{
music[item.Key] = (from p in item.Value.Where(clips.ContainsKey)
select clips[p]).ToArray();
}
foreach (KeyValuePair<string, string[]> item2 in plan.Sfx)
{
sfx[item2.Key] = (from p in item2.Value.Where(clips.ContainsKey)
select clips[p]).ToArray();
}
Ready = true;
scans = 0;
ManualLogSource log = ((BasePlugin)Plugin.Instance).Log;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(103, 2, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Audio pack ready: ");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(clips.Count);
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" clips, ");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<float>((float)decodedBytes / 1048576f, "F1");
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" MiB budgeted (music streams). Missing or invalid entries retain stock audio.");
}
log.LogInfo(val2);
}
private void Prune()
{
int[] array = (from p in snapshots
where !Object.op_Implicit((Object)(object)p.Value.Sound)
select p.Key).ToArray();
foreach (int key in array)
{
snapshots.Remove(key);
}
array = (from p in selections
where !Object.op_Implicit((Object)(object)p.Value.Music)
select p.Key).ToArray();
foreach (int key2 in array)
{
DestroySelection(selections[key2]);
selections.Remove(key2);
}
pools.RemoveAll((AudioSources p) => !Object.op_Implicit((Object)(object)p));
array = (from p in muted
where !Object.op_Implicit((Object)(object)p.Value)
select p.Key).ToArray();
foreach (int key3 in array)
{
muted.Remove(key3);
}
PruneContextSounds();
}
private void Scan()
{
Prune();
foreach (AudioSources pool in Resources.FindObjectsOfTypeAll<AudioSources>())
{
if (Object.op_Implicit((Object)(object)pool) && !pools.Any((AudioSources p) => ((Il2CppObjectBase)p).Pointer == ((Il2CppObjectBase)pool).Pointer))
{
pools.Add(pool);
}
}
foreach (MainMenu item in Resources.FindObjectsOfTypeAll<MainMenu>())
{
if (Object.op_Implicit((Object)(object)item))
{
RegisterMenu(item);
}
}
foreach (Music item2 in Resources.FindObjectsOfTypeAll<Music>())
{
if (Object.op_Implicit((Object)(object)item2))
{
RegisterMusic(item2);
}
}
RegisterFields<BaseButton>(new(string, string)[2]
{
("clickSound", "ui_click"),
("hoverSound", "ui_hover")
});
RegisterFields<Toggle>(new(string, string)[1] { ("clickSound", "ui_click") });
RegisterFields<BuildingPlacer>(new(string, string)[2]
{
("placeSound", "building_place"),
("rotateSound", "building_rotate")
});
RegisterFields<BuildingBulkPlacer>(new(string, string)[2]
{
("placeSound", "building_place"),
("rotateSound", "building_rotate")
});
RegisterFields<HarvestInterface>(new(string, string)[4]
{
("moveDiceSound", "dice_move"),
("skullDiceSound", "dice_skull"),
("harvestResourceSound", "harvest_resource"),
("dicePopSound", "dice_pop")
});
RegisterFields<ResourceStorageInterface>(new(string, string)[1] { ("incrementSound", "harvest_resource") });
RegisterFields<ResourceParticle>(new(string, string)[1] { ("resourceSound", "harvest_resource") });
foreach (Sound item3 in Resources.FindObjectsOfTypeAll<Sound>())
{
if (Object.op_Implicit((Object)(object)item3))
{
ApplySound(item3);
}
}
}
[HideFromIl2Cpp]
private void RegisterFields<T>(params (string Property, string Event)[] fields) where T : Object
{
(PropertyInfo, string)[] array = fields.Select(((string Property, string Event) f) => (Property: typeof(T).GetProperty(f.Property), Event: f.Event)).ToArray();
foreach (T item in Resources.FindObjectsOfTypeAll<T>())
{
(PropertyInfo, string)[] array2 = array;
for (int num = 0; num < array2.Length; num++)
{
(PropertyInfo, string) tuple = array2[num];
object? obj = tuple.Item1?.GetValue(item);
Sound val = (Sound)((obj is Sound) ? obj : null);
if (val == null || !Object.op_Implicit((Object)(object)val))
{
continue;
}
Snapshot snapshot = Remember(val);
if (snapshot.Ambiguous)
{
continue;
}
if (snapshot.FieldAssigned && snapshot.Event != tuple.Item2)
{
if (snapshot.Changed)
{
RebindPlaying(snapshot, snapshot.Clips, replacing: false);
snapshot.Restore();
}
snapshot.Ambiguous = true;
Plugin.Instance.Debug("Shared Sound '" + ((Object)val).name + "' has different semantic roles; keeping stock audio.");
}
else
{
if (snapshot.Event != tuple.Item2 && snapshot.Changed)
{
RebindPlaying(snapshot, snapshot.Clips, replacing: false);
snapshot.Restore();
}
snapshot.Event = tuple.Item2;
snapshot.FieldAssigned = true;
}
}
}
}
public void RegisterMenu(MainMenu menu)
{
activeMenu = menu;
if (Object.op_Implicit((Object)(object)menu.menuMusic))
{
Remember(menu.menuMusic).Event = "music:menu";
ApplySound(menu.menuMusic);
}
}
internal void DisableMenu(MainMenu menu)
{
if (!Object.op_Implicit((Object)(object)activeMenu) || ((Il2CppObjectBase)activeMenu).Pointer != ((Il2CppObjectBase)menu).Pointer)
{
return;
}
if (menuSlot != null && menuSlot.CurrentTicket == menuTicket)
{
Snapshot? snapshot = menuSnapshot;
if (snapshot != null && snapshot.Changed && Object.op_Implicit((Object)(object)menuSlot.audioSource))
{
menuSlot.audioSource.Stop();
}
}
activeMenu = null;
menuSlot = null;
menuPool = null;
}
internal void PreparePlay(Sound sound)
{
ApplySound(sound);
if (Object.op_Implicit((Object)(object)sound) && snapshots.TryGetValue(((Object)sound).GetInstanceID(), out Snapshot value) && value.Changed && !(value.Event != "music:menu"))
{
AudioClip[] array = Playlist("menu");
if (array != null && array.Length != 0)
{
sound.clips = new Il2CppReferenceArray<AudioClip>((AudioClip[])(object)new AudioClip[1] { array[Bag("menu").Next(array.Length)] });
sound.loop = false;
}
}
}
internal void CaptureMenuSource(AudioSources pool, Sound sound, AllocatedSource source)
{
object obj;
if (source == null)
{
obj = null;
}
else
{
PooledSource pooledSource = source.pooledSource;
obj = ((pooledSource != null) ? pooledSource.audioSource : null);
}
AudioSource val = (AudioSource)obj;
if (val != null && Object.op_Implicit((Object)(object)val))
{
SetMuted(val, ((Object.op_Implicit((Object)(object)sound) && effectSoundIds.Contains(((Object)sound).GetInstanceID())) ? new float?(Plugin.SfxVolume.Value) : ((Object.op_Implicit((Object)(object)sound) && ownedSounds.Contains(((Object)sound).GetInstanceID())) ? new float?(Plugin.MusicVolume.Value) : ((Object.op_Implicit((Object)(object)sound) && snapshots.TryGetValue(((Object)sound).GetInstanceID(), out Snapshot value) && value.Changed) ? new float?(value.Event.StartsWith("music:") ? Plugin.MusicVolume.Value : Plugin.SfxVolume.Value) : ((float?)null)))) == 0f);
}
if (Object.op_Implicit((Object)(object)sound) && source != null && snapshots.TryGetValue(((Object)sound).GetInstanceID(), out Snapshot value2) && value2.Changed && !(value2.Event != "music:menu"))
{
menuPool = pool;
menuSlot = source.pooledSource;
menuTicket = source.ticket;
menuSnapshot = value2;
}
}
private void SetMuted(AudioSource source, bool mute)
{
int instanceID = ((Object)source).GetInstanceID();
if (mute)
{
if (!source.mute)
{
source.mute = true;
muted[instanceID] = source;
}
}
else if (muted.Remove(instanceID))
{
source.mute = false;
}
}
private void UpdateMenuPlaylist()
{
if (!Ready || !Plugin.MusicEnabled.Value || !Object.op_Implicit((Object)(object)activeMenu) || !((Behaviour)activeMenu).isActiveAndEnabled)
{
return;
}
Snapshot? snapshot = menuSnapshot;
if (snapshot == null || !snapshot.Changed || menuSlot == null || !Object.op_Implicit((Object)(object)menuPool) || AudioListener.pause)
{
return;
}
if (menuSlot.CurrentTicket != menuTicket)
{
menuPool.Play(menuSnapshot.Sound, new Nullable<Vector3>());
return;
}
AudioSource audioSource = menuSlot.audioSource;
if (Object.op_Implicit((Object)(object)audioSource) && !audioSource.isPlaying)
{
AudioClip[] array = Playlist("menu");
if (array != null && array.Length != 0)
{
audioSource.clip = array[Bag("menu").Next(array.Length)];
audioSource.loop = false;
audioSource.Play();
}
}
}
public void RegisterMusic(Music item)
{
int instanceID = ((Object)item).GetInstanceID();
if (!selections.ContainsKey(instanceID))
{
selections.Add(instanceID, new Selection
{
Music = item,
OriginalDuration = item.transitionDuration
});
}
}
public void ForgetMusic(Music item)
{
if (!selections.Remove(((Object)item).GetInstanceID(), out Selection value))
{
return;
}
if (OwnsSound(item.currentTrack))
{
StopIfAlive(item.currentSource);
}
int num = 0;
while (item.dyingSources != null && num < item.dyingSources.Count)
{
if (OwnedAllocation(item.dyingSources[num]))
{
StopIfAlive(item.dyingSources[num]);
}
num++;
}
item.dyingSources?.Clear();
item.transitionDuration = value.OriginalDuration;
DestroySelection(value);
}
[HideFromIl2Cpp]
private Snapshot Remember(Sound sound)
{
int instanceID = ((Object)sound).GetInstanceID();
if (snapshots.TryGetValue(instanceID, out Snapshot value))
{
return value;
}
Snapshot snapshot = new Snapshot
{
Sound = sound,
Clips = (((IEnumerable<AudioClip>)sound.clips)?.ToArray() ?? Array.Empty<AudioClip>()),
Min = sound.minVolume,
Max = sound.maxVolume,
MinPitch = sound.minPitch,
MaxPitch = sound.maxPitch,
Loop = sound.loop
};
snapshots.Add(instanceID, snapshot);
return snapshot;
}
public void ApplySound(Sound sound)
{
if (stopped || !Object.op_Implicit((Object)(object)sound) || ownedSounds.Contains(((Object)sound).GetInstanceID()) || effectSoundIds.Contains(((Object)sound).GetInstanceID()))
{
return;
}
if (Plugin.DebugLog.Value && seen.Add(((Object)sound).GetInstanceID()))
{
Plugin.Instance.Debug("Sound '" + ((Object)sound).name + "': " + string.Join(", ", ((IEnumerable<AudioClip>)sound.clips)?.Select((AudioClip c) => (!Object.op_Implicit((Object)(object)c)) ? "null" : ((Object)c).name) ?? Array.Empty<string>()));
}
if (!Ready || plan == null)
{
return;
}
Snapshot snapshot = Remember(sound);
if (snapshot.Ambiguous)
{
return;
}
if (snapshot.Event == "")
{
string[] array = (from c in snapshot.Clips
where Object.op_Implicit((Object)(object)c)
select plan.Map.ClipEvents.GetValueOrDefault(((Object)c).name)).Distinct().ToArray();
if (array.Length == 1 && array[0] != null && !array[0].StartsWith("music:"))
{
snapshot.Event = array[0];
}
}
bool flag = snapshot.Event.StartsWith("music:");
AudioClip[] array2 = (flag ? Playlist(snapshot.Event.Substring(6)) : sfx.GetValueOrDefault(snapshot.Event));
if (!(flag ? Plugin.MusicEnabled.Value : Plugin.SfxEnabled.Value) || array2 == null || array2.Length == 0)
{
if (snapshot.Changed)
{
RebindPlaying(snapshot, snapshot.Clips, replacing: false);
snapshot.Restore();
}
}
else if (!snapshot.Changed)
{
sound.clips = new Il2CppReferenceArray<AudioClip>(array2);
float num = Math.Max(0.0001f, flag ? Plugin.MusicVolume.Value : Plugin.SfxVolume.Value);
sound.minVolume = snapshot.Min * num;
sound.maxVolume = snapshot.Max * num;
if (flag)
{
sound.minPitch = 1f;
sound.maxPitch = 1f;
}
snapshot.Changed = true;
RebindPlaying(snapshot, array2, replacing: true);
Plugin.Instance.Debug($"Mapped '{((Object)sound).name}' => {snapshot.Event} ({array2.Length} variants)");
}
}
[HideFromIl2Cpp]
private void RebindPlaying(Snapshot snap, AudioClip[] target, bool replacing)
{
if (target.Length == 0)
{
return;
}
AudioClip[] source = (AudioClip[])(replacing ? snap.Clips : ((snap.Event == "music:menu") ? ((Array)(Playlist("menu") ?? ((IEnumerable<AudioClip>)snap.Sound.clips).ToArray())) : ((Array)((IEnumerable<AudioClip>)snap.Sound.clips).ToArray())));
foreach (AudioSources pool in pools)
{
if (!Object.op_Implicit((Object)(object)pool) || pool.pool == null)
{
continue;
}
foreach (PooledSource item in (Il2CppArrayBase<PooledSource>)(object)pool.pool)
{
AudioSource source2 = ((item != null) ? item.audioSource : null);
if (!((Object)(object)source2 == (Object)null) && Object.op_Implicit((Object)(object)source2) && source2.isPlaying && Object.op_Implicit((Object)(object)source2.clip) && source.Any((AudioClip c) => Object.op_Implicit((Object)(object)c) && ((Il2CppObjectBase)c).Pointer == ((Il2CppObjectBase)source2.clip).Pointer))
{
bool flag = snap.Event == "music:menu";
source2.Stop();
source2.clip = target[(replacing && flag) ? Bag("menu").Next(target.Length) : 0];
source2.loop = !(replacing && flag) && snap.Loop;
if (replacing && flag)
{
menuPool = pool;
menuSlot = item;
menuTicket = item.CurrentTicket;
menuSnapshot = snap;
}
float num = (snap.Event.StartsWith("music:") ? Plugin.MusicVolume.Value : Plugin.SfxVolume.Value);
source2.volume = (replacing ? (snap.Min * Math.Max(0.0001f, num)) : snap.Min);
SetMuted(source2, replacing && num == 0f);
source2.Play();
}
}
}
}
[HideFromIl2Cpp]
private AudioClip[]? Playlist(string context)
{
return music.GetValueOrDefault(context);
}
[HideFromIl2Cpp]
private ShuffleBag Bag(string context)
{
if (!bags.TryGetValue(context, out ShuffleBag value))
{
bags.Add(context, value = new ShuffleBag());
}
return value;
}
private static bool Same(Sound? a, Sound? b)
{
if (Object.op_Implicit((Object)(object)a) && Object.op_Implicit((Object)(object)b))
{
return ((Il2CppObjectBase)a).Pointer == ((Il2CppObjectBase)b).Pointer;
}
return false;
}
private static bool SamePhase(ITurnPhase? phase, Component? other)
{
if (phase != null && Object.op_Implicit((Object)(object)other))
{
return ((Il2CppObjectBase)phase).Pointer == ((Il2CppObjectBase)other).Pointer;
}
return false;
}
public Sound Track(Music item, Sound stock)
{
if (Object.op_Implicit((Object)(object)stock))
{
LastStockTrackPointer = ((Il2CppObjectBase)stock).Pointer;
}
if (stopped || !Ready || !Plugin.MusicEnabled.Value || !Object.op_Implicit((Object)(object)stock))
{
return stock;
}
RegisterMusic(item);
Selection selection = selections[((Object)item).GetInstanceID()];
GameState gameState = item.GameState;
string text;
if (!Plugin.Dynamic.Value)
{
text = "menu";
}
else if (Same(stock, item.winningTrack))
{
text = "victory";
}
else if (Same(stock, item.loosingTrack))
{
text = "defeat";
}
else if (!Object.op_Implicit((Object)(object)gameState) || !gameState.GameStarted)
{
text = "lobby";
}
else
{
ITurnPhase turnPhase = gameState.TurnPhase;
if (SamePhase(turnPhase, (Component?)(object)gameState.harvestPhase))
{
text = "harvest";
}
else if (!SamePhase(turnPhase, (Component?)(object)gameState.disastersPhase))
{
text = (SamePhase(turnPhase, (Component?)(object)gameState.buildPhase) ? "building" : (SamePhase(turnPhase, (Component?)(object)gameState.startPhase) ? "start" : ((!SamePhase(turnPhase, (Component?)(object)gameState.combatPhase)) ? "harvest" : ((Same(stock, item.combatTrack) || Same(stock, item.combatDecisiveTrack)) ? "combat" : "planning"))));
}
else
{
object obj;
switch (selection.Context)
{
default:
obj = "harvest";
break;
case "menu":
case "lobby":
case "harvest":
case "building":
case "start":
obj = selection.Context;
break;
}
text = (string)obj;
}
}
if (selection.Context != text)
{
selection.Context = text;
selection.Selected = null;
selection.JingleDone = false;
selection.HasPlayed = false;
LastContext = text;
Plugin.Instance.Debug("Music context: " + text);
}
bool flag = text == "start" || text == "victory" || text == "defeat";
if (Object.op_Implicit((Object)(object)selection.Selected) && selection.HasPlayed && !item.currentSource.IsActive && flag)
{
selection.JingleDone = true;
}
string text2 = ((flag && selection.JingleDone) ? "harvest" : text);
AudioClip[] array = Playlist(text2);
if (array == null || array.Length == 0)
{
return stock;
}
if (Object.op_Implicit((Object)(object)selection.Selected) && !selection.HasPlayed)
{
return selection.Selected;
}
if (Object.op_Implicit((Object)(object)selection.Selected) && item.currentSource.IsActive && Same(item.currentTrack, selection.Selected))
{
return selection.Selected;
}
string key = text2 + ":" + ((Object)stock).GetInstanceID();
if (!selection.Sounds.TryGetValue(key, out Sound value))
{
value = Object.Instantiate<Sound>(stock);
((Object)value).name = "VillageAudio/" + text2;
((Object)value).hideFlags = (HideFlags)61;
selection.Sounds.Add(key, value);
created.Add(value);
ownedSounds.Add(((Object)value).GetInstanceID());
}
value.clips = new Il2CppReferenceArray<AudioClip>((AudioClip[])(object)new AudioClip[1] { array[Bag(text2).Next(array.Length)] });
value.minVolume = stock.minVolume * Math.Max(0.0001f, Plugin.MusicVolume.Value);
value.maxVolume = stock.maxVolume * Math.Max(0.0001f, Plugin.MusicVolume.Value);
value.minPitch = 1f;
value.maxPitch = 1f;
value.loop = false;
selection.Selected = value;
return value;
}
internal void Played(Music item, Sound track)
{
if (selections.TryGetValue(((Object)item).GetInstanceID(), out Selection value) && Same(value.Selected, track))
{
value.HasPlayed = true;
}
}
private static bool OwnedAllocation(AllocatedSource source)
{
object obj;
if (source == null)
{
obj = null;
}
else
{
PooledSource pooledSource = source.pooledSource;
obj = ((pooledSource != null) ? pooledSource.audioSource : null);
}
AudioSource val = (AudioSource)obj;
if ((Object)(object)val == (Object)null || !Object.op_Implicit((Object)(object)val))
{
return false;
}
AudioClip clip = val.clip;
if ((Object)(object)clip != (Object)null && Object.op_Implicit((Object)(object)clip))
{
return ((Object)clip).name.StartsWith("VillageAudio/", StringComparison.Ordinal);
}
return false;
}
private static void StopIfAlive(AllocatedSource source)
{
object obj;
if (source == null)
{
obj = null;
}
else
{
PooledSource pooledSource = source.pooledSource;
obj = ((pooledSource != null) ? pooledSource.audioSource : null);
}
AudioSource val = (AudioSource)obj;
if ((Object)(object)val != (Object)null && Object.op_Implicit((Object)(object)val))
{
source.Stop();
}
}
private void RefreshSettings()
{
if (!Ready)
{
return;
}
Snapshot[] array = snapshots.Values.ToArray();
foreach (Snapshot snapshot in array)
{
if (Object.op_Implicit((Object)(object)snapshot.Sound))
{
if (snapshot.Changed)
{
RebindPlaying(snapshot, snapshot.Clips, replacing: false);
snapshot.Restore();
}
ApplySound(snapshot.Sound);
}
}
foreach (Selection value in selections.Values)
{
if (Object.op_Implicit((Object)(object)value.Music))
{
value.Selected = null;
value.HasPlayed = false;
Sound currentTrack = value.Music.currentTrack;
if ((Object)(object)currentTrack != (Object)null && Object.op_Implicit((Object)(object)currentTrack) && ownedSounds.Contains(((Object)currentTrack).GetInstanceID()))
{
value.Music.currentTrack = null;
}
value.Music.transitionDuration = value.OriginalDuration;
value.Music.UpdateTrack();
}
}
RefreshContextSounds();
}
[HideFromIl2Cpp]
private void DestroySelection(Selection state)
{
foreach (Sound sound in state.Sounds.Values)
{
if (Object.op_Implicit((Object)(object)sound))
{
ownedSounds.Remove(((Object)sound).GetInstanceID());
created.RemoveAll((Sound c) => !Object.op_Implicit((Object)(object)c) || ((Il2CppObjectBase)c).Pointer == ((Il2CppObjectBase)sound).Pointer);
Object.Destroy((Object)(object)sound);
}
}
state.Sounds.Clear();
}
public void Shutdown()
{
if (stopped)
{
return;
}
stopped = true;
StopContextAllocations();
UnityWebRequest? obj = request;
if (obj != null)
{
obj.Abort();
}
UnityWebRequest? obj2 = request;
if (obj2 != null)
{
obj2.Dispose();
}
request = null;
foreach (Snapshot value in snapshots.Values)
{
if (Object.op_Implicit((Object)(object)value.Sound) && value.Changed)
{
RebindPlaying(value, value.Clips, replacing: false);
value.Restore();
}
}
foreach (Selection value2 in selections.Values)
{
if (!Object.op_Implicit((Object)(object)value2.Music))
{
continue;
}
bool flag = OwnsSound(value2.Music.currentTrack);
if (flag)
{
StopIfAlive(value2.Music.currentSource);
}
int num = 0;
while (value2.Music.dyingSources != null && num < value2.Music.dyingSources.Count)
{
if (OwnedAllocation(value2.Music.dyingSources[num]))
{
StopIfAlive(value2.Music.dyingSources[num]);
}
num++;
}
value2.Music.dyingSources?.Clear();
value2.Music.transitionDuration = value2.OriginalDuration;
if (flag)
{
value2.Music.currentTrack = null;
value2.Music.UpdateTrack();
}
}
foreach (Sound item in created)
{
if (Object.op_Implicit((Object)(object)item))
{
Object.Destroy((Object)(object)item);
}
}
foreach (AudioClip value3 in clips.Values)
{
if (Object.op_Implicit((Object)(object)value3))
{
value3.UnloadAudioData();
Object.Destroy((Object)(object)value3);
}
}
created.Clear();
clips.Clear();
snapshots.Clear();
selections.Clear();
ownedSounds.Clear();
foreach (AudioSource value4 in muted.Values)
{
if (Object.op_Implicit((Object)(object)value4))
{
value4.mute = false;
}
}
muted.Clear();
ClearContextSounds();
Ready = false;
}
public void OnDestroy()
{
Shutdown();
if (Object.ReferenceEquals((Object)(object)Plugin.Runtime, (Object)(object)this))
{
Plugin.Runtime = null;
}
}
internal void RecordResource(Resource resource)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
resourceContext = resource;
resourceFrame = Time.frameCount;
}
internal void RecordBuilding(string type)
{
buildingContext = type;
buildingFrame = Time.frameCount;
}
internal void RecordUnit(string type)
{
unitContext = type;
unitFrame = Time.frameCount;
}
internal void ForgetBuildingVisual(BuildingVisual visual)
{
if (Object.op_Implicit((Object)(object)visual))
{
buildingHealth.Remove(((Object)visual).GetInstanceID());
}
}
internal bool RouteContextSound(ref Sound sound)
{
//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
if (stopped || !Ready || !Plugin.SfxEnabled.Value || plan == null || !Object.op_Implicit((Object)(object)sound) || effectSoundIds.Contains(((Object)sound).GetInstanceID()))
{
return true;
}
if (!snapshots.TryGetValue(((Object)sound).GetInstanceID(), out Snapshot value))
{
return true;
}
string text = null;
if (value.Event == "harvest_resource" && resourceContext.HasValue && resourceFrame == Time.frameCount)
{
text = plan.Map.ResourceEvents.GetValueOrDefault(((object)resourceContext.Value/*cast due to .constrained prefix*/).ToString());
resourceContext = null;
if (text != null && sfx.TryGetValue(text, out AudioClip[] value2) && value2.Length != 0)
{
if (resourcePlayedAt.TryGetValue(text, out var value3) && Time.unscaledTime - value3 < 0.35f)
{
SuppressedResourceRepeats++;
return false;
}
resourcePlayedAt[text] = Time.unscaledTime;
}
}
else if (value.Event == "building_place" && buildingContext != null && buildingFrame == Time.frameCount)
{
text = plan.Map.BuildingEvents.GetValueOrDefault(buildingContext);
buildingContext = null;
}
else if (value.Event == "unit_die" && unitContext != null && unitFrame == Time.frameCount)
{
object obj;
if (!(unitContext == "ARCHER"))
{
string text2 = unitContext;
obj = ((text2 == "SORCERER" || text2 == "WARLOCK") ? "unit_die_wizard" : "unit_die");
}
else
{
obj = "unit_die_archer";
}
text = (string)obj;
unitContext = null;
}
if (text == null)
{
return true;
}
Sound val = EffectSound(sound, text);
if (Object.op_Implicit((Object)(object)val))
{
sound = val;
}
return true;
}
[HideFromIl2Cpp]
private Sound? EffectSound(Sound stock, string key)
{
if (!sfx.TryGetValue(key, out AudioClip[] value) || value.Length == 0)
{
return null;
}
AudioClip[] array = value;
foreach (AudioClip val in array)
{
effectClipPointers.Add(((Il2CppObjectBase)val).Pointer);
}
Snapshot snapshot = Remember(stock);
string key2 = ((Object)stock).GetInstanceID() + ":" + key;
if (!effectSounds.TryGetValue(key2, out Sound value2))
{
value2 = Object.Instantiate<Sound>(stock);
((Object)value2).name = "VillageAudio/SFX/" + key;
((Object)value2).hideFlags = (HideFlags)61;
effectSounds.Add(key2, value2);
effectSoundIds.Add(((Object)value2).GetInstanceID());
created.Add(value2);
}
value2.clips = new Il2CppReferenceArray<AudioClip>((AudioClip[])(object)new AudioClip[1] { value[Bag("sfx:" + key).Next(value.Length)] });
value2.minPitch = 1f;
value2.maxPitch = 1f;
value2.loop = false;
value2.minVolume = snapshot.Min * Math.Max(0.0001f, Plugin.SfxVolume.Value);
value2.maxVolume = snapshot.Max * Math.Max(0.0001f, Plugin.SfxVolume.Value);
LastEffectEvent = key;
LastEffectClip = ((Object)((Il2CppArrayBase<AudioClip>)(object)value2.clips)[0]).name;
return value2;
}
internal void ObserveBuildingHealth(BuildingVisual visual, float health)
{
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
if (stopped || !Ready || !Object.op_Implicit((Object)(object)visual))
{
return;
}
Scene val = ((Component)visual).gameObject.scene;
if (!((Scene)(ref val)).IsValid())
{
return;
}
int instanceID = ((Object)visual).GetInstanceID();
(BuildingVisual, float) value;
bool num = buildingHealth.TryGetValue(instanceID, out value) && value.Item2 > 0f && health <= 0f;
buildingHealth[instanceID] = (visual, health);
if (num && Plugin.SfxEnabled.Value && ((Behaviour)visual).isActiveAndEnabled && Object.op_Implicit((Object)(object)visual.audioSources) && Object.op_Implicit((Object)(object)visual.hitSound))
{
Sound val2 = EffectSound(visual.hitSound, "building_destroyed");
if (Object.op_Implicit((Object)(object)val2))
{
visual.audioSources.Play(val2, new Nullable<Vector3>(((Component)visual).transform.position));
}
}
}
private void PruneContextSounds()
{
int[] array = (from p in buildingHealth
where !Object.op_Implicit((Object)(object)p.Value.Visual)
select p.Key).ToArray();
foreach (int key in array)
{
buildingHealth.Remove(key);
}
string[] array2 = effectSounds.Keys.Where((string k) => !snapshots.ContainsKey(int.Parse(k.Split(':')[0]))).ToArray();
foreach (string key2 in array2)
{
Sound sound = effectSounds[key2];
effectSounds.Remove(key2);
if (Object.op_Implicit((Object)(object)sound))
{
effectSoundIds.Remove(((Object)sound).GetInstanceID());
created.RemoveAll((Sound c) => !Object.op_Implicit((Object)(object)c) || ((Il2CppObjectBase)c).Pointer == ((Il2CppObjectBase)sound).Pointer);
Object.Destroy((Object)(object)sound);
}
}
}
private void RefreshContextSounds()
{
if (!Plugin.SfxEnabled.Value)
{
StopContextAllocations();
resourcePlayedAt.Clear();
}
}
private void StopContextAllocations()
{
foreach (AudioSources pool in pools)
{
if (!Object.op_Implicit((Object)(object)pool) || pool.pool == null)
{
continue;
}
foreach (PooledSource item in (Il2CppArrayBase<PooledSource>)(object)pool.pool)
{
AudioSource val = ((item != null) ? item.audioSource : null);
if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.clip) && effectClipPointers.Contains(((Il2CppObjectBase)val.clip).Pointer))
{
val.Stop();
}
}
}
}
private void ClearContextSounds()
{
effectSounds.Clear();
effectSoundIds.Clear();
effectClipPointers.Clear();
resourcePlayedAt.Clear();
buildingHealth.Clear();
}
}
[BepInPlugin("io.github.mikhasan.dicekingdoms.villageaudio", "Village Audio Overhaul", "1.1.0")]
[BepInProcess("Dice-Kingdoms.exe")]
public sealed class Plugin : BasePlugin
{
public const string Id = "io.github.mikhasan.dicekingdoms.villageaudio";
internal static Plugin Instance = null;
internal static ConfigEntry<bool> MusicEnabled = null;
internal static ConfigEntry<bool> SfxEnabled = null;
internal static ConfigEntry<bool> Dynamic = null;
internal static ConfigEntry<bool> Fade = null;
internal static ConfigEntry<bool> DebugLog = null;
internal static ConfigEntry<float> MusicVolume = null;
internal static ConfigEntry<float> SfxVolume = null;
internal static ConfigEntry<float> FadeSeconds = null;
internal static AudioRuntime? Runtime;
internal static string Folder = "";
internal static volatile bool Dirty;
private Harmony? harmony;
private AudioRuntime? component;
private readonly HashSet<string> errors = new HashSet<string>();
public override void Load()
{
//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
//IL_00d2: Expected O, but got Unknown
//IL_0105: Unknown result type (might be due to invalid IL or missing references)
//IL_010f: Expected O, but got Unknown
//IL_0142: Unknown result type (might be due to invalid IL or missing references)
//IL_014c: Expected O, but got Unknown
//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
//IL_01c7: Expected O, but got Unknown
Instance = this;
Folder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
MusicEnabled = ((BasePlugin)this).Config.Bind<bool>("General", "EnableMusicReplacement", true, "Replace music when a valid local track exists.");
SfxEnabled = ((BasePlugin)this).Config.Bind<bool>("General", "EnableSFXReplacement", true, "Replace only mapped effects with valid local files.");
Dynamic = ((BasePlugin)this).Config.Bind<bool>("General", "EnableDynamicMusic", true, "Select music by the actual game phase. When false use the menu playlist throughout a match.");
Fade = ((BasePlugin)this).Config.Bind<bool>("Music", "EnableCrossfade", true, "Use the game's existing transition and pooled sources.");
FadeSeconds = ((BasePlugin)this).Config.Bind<float>("Music", "CrossfadeSeconds", 1.5f, new ConfigDescription("Transition duration in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 10f), Array.Empty<object>()));
MusicVolume = ((BasePlugin)this).Config.Bind<float>("Volume", "MusicVolume", 0.75f, new ConfigDescription("Music multiplier; also respects the game's Music and Master mixer settings.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
SfxVolume = ((BasePlugin)this).Config.Bind<float>("Volume", "SFXVolume", 0.85f, new ConfigDescription("Effect multiplier; also respects the game's mixer settings.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
DebugLog = ((BasePlugin)this).Config.Bind<bool>("Diagnostics", "DebugLogging", false, "Log first-seen sound IDs and phase changes.");
((BasePlugin)this).Config.SettingChanged += delegate
{
Dirty = true;
};
ClassInjector.RegisterTypeInIl2Cpp<AudioFileHandler>();
component = ((BasePlugin)this).AddComponent<AudioRuntime>();
Runtime = component;
harmony = new Harmony("io.github.mikhasan.dicekingdoms.villageaudio");
Patch(typeof(Music), "get_Track", "Track", prefix: false);
Patch(typeof(Music), "Transition", "TransitionPrefix", prefix: true);
Patch(typeof(Music), "Transition", "TransitionPostfix", prefix: false);
Patch(typeof(Music), "OnEnable", "MusicEnable", prefix: true);
Patch(typeof(Music), "OnDisable", "MusicDisable", prefix: false);
Patch(typeof(MainMenu), "Awake", "Menu", prefix: true);
Patch(typeof(MainMenu), "OnEnable", "Menu", prefix: true);
Patch(typeof(MainMenu), "OnDisable", "MenuDisable", prefix: false);
Patch(typeof(AudioSources), "Play", "Play", prefix: true);
Patch(typeof(AudioSources), "Play", "Played", prefix: false);
Patch(typeof(ResourceStorageInterface), "Increment", "ResourceIncrement", prefix: false);
Patch(typeof(ResourceParticle), "OnEnd", "ResourceParticle", prefix: true);
Patch(typeof(BuildingPlacer), "Place", "Placement", prefix: true);
Patch(typeof(BuildingBulkPlacer), "Place", "BulkPlacement", prefix: true);
Patch(typeof(UnitVisual), "OnDie", "UnitDeath", prefix: true);
Patch(typeof(BuildingVisual), "SetHealth", "BuildingHealth", prefix: false);
Patch(typeof(BuildingVisual), "OnDisable", "BuildingDisabled", prefix: false);
Patch(typeof(AllocatedSource), "set_Pitch", "CollectorPitch", prefix: true);
((BasePlugin)this).Log.LogInfo((object)"Village Audio Overhaul 1.1.0: resource/type-specific native SFX routing; disasters preserve calm music.");
}
private void Patch(Type type, string method, string handler, bool prefix)
{
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Expected O, but got Unknown
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Expected O, but got Unknown
MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null) ?? throw new MissingMethodException(type.FullName, method);
HarmonyMethod val = new HarmonyMethod(typeof(Hooks), handler, (Type[])null);
harmony.Patch((MethodBase)methodInfo, prefix ? val : null, prefix ? null : val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
ManualLogSource log = ((BasePlugin)this).Log;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(8, 2, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Hooked ");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(type.Name);
((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(".");
((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(method);
}
log.LogInfo(val2);
}
internal void Error(Exception ex)
{
string text = ex.GetType().Name + ": " + ex.Message;
if (errors.Add(text))
{
((BasePlugin)this).Log.LogWarning((object)(DebugLog.Value ? ex.ToString() : text));
}
}
internal void Debug(string message)
{
if (DebugLog.Value)
{
((BasePlugin)this).Log.LogInfo((object)message);
}
}
public override bool Unload()
{
Runtime?.Shutdown();
Runtime = null;
Harmony? obj = harmony;
if (obj != null)
{
obj.UnpatchSelf();
}
if ((Object)(object)component != (Object)null)
{
Object.Destroy((Object)(object)component);
}
return true;
}
}
internal static class Hooks
{
internal static void Track(Music __instance, ref Sound __result)
{
try
{
if ((Object)(object)Plugin.Runtime != (Object)null)
{
__result = Plugin.Runtime.Track(__instance, __result);
}
}
catch (Exception ex)
{
Plugin.Instance.Error(ex);
}
}
internal static void Menu(MainMenu __instance)
{
try
{
Plugin.Runtime?.RegisterMenu(__instance);
}
catch (Exception ex)
{
Plugin.Instance.Error(ex);
}
}
internal static void MusicEnable(Music __instance)
{
try
{
Plugin.Runtime?.RegisterMusic(__instance);
}
catch (Exception ex)
{
Plugin.Instance.Error(ex);
}
}
internal static void MusicDisable(Music __instance)
{
try
{
Plugin.Runtime?.ForgetMusic(__instance);
}
catch (Exception ex)
{
Plugin.Instance.Error(ex);
}
}
internal static void MenuDisable(MainMenu __instance)
{
try
{
Plugin.Runtime?.DisableMenu(__instance);
}
catch (Exception ex)
{
Plugin.Instance.Error(ex);
}
}
internal static bool Play(ref Sound sound, ref AllocatedSource __result)
{
try
{
Plugin.Runtime?.PreparePlay(sound);
if ((Object)(object)Plugin.Runtime != (Object)null && !Plugin.Runtime.RouteContextSound(ref sound))
{
__result = AllocatedSource.noop;
return false;
}
}
catch (Exception ex)
{
Plugin.Instance.Error(ex);
}
return true;
}
internal static void ResourceIncrement(ResourceStorageInterface __instance, int amount)
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
try
{
if (amount > 0)
{
Plugin.Runtime?.RecordResource(__instance.resource);
}
}
catch (Exception ex)
{
Plugin.Instance.Error(ex);
}
}
internal static void ResourceParticle(ResourceParticle __instance)
{
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Invalid comparison between Unknown and I4
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
try
{
if (__instance.isForLocalPlayer && (int)__instance.resource != 7)
{
Plugin.Runtime?.RecordResource(__instance.resource);
}
}
catch (Exception ex)
{
Plugin.Instance.Error(ex);
}
}
internal static void Placement(BuildingPlacer __instance)
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
try
{
if (__instance.CurrentBuildingType != null)
{
Plugin.Runtime?.RecordBuilding(((object)__instance.CurrentBuildingType.name/*cast due to .constrained prefix*/).ToString());
}
}
catch (Exception ex)
{
Plugin.Instance.Error(ex);
}
}
internal static void BulkPlacement(BuildingBulkPlacer __instance)
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
try
{
if (__instance.CurrentBuildingType != null)
{
Plugin.Runtime?.RecordBuilding(((object)__instance.CurrentBuildingType.name/*cast due to .constrained prefix*/).ToString());
}
}
catch (Exception ex)
{
Plugin.Instance.Error(ex);
}
}
internal static void UnitDeath(UnitVisual __instance)
{
//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)
try
{
Plugin.Runtime?.RecordUnit(((object)__instance.unitType/*cast due to .constrained prefix*/).ToString());
}
catch (Exception ex)
{
Plugin.Instance.Error(ex);
}
}
internal static void BuildingHealth(BuildingVisual __instance, float health)
{
try
{
Plugin.Runtime?.ObserveBuildingHealth(__instance, health);
}
catch (Exception ex)
{
Plugin.Instance.Error(ex);
}
}
internal static void BuildingDisabled(BuildingVisual __instance)
{
try
{
Plugin.Runtime?.ForgetBuildingVisual(__instance);
}
catch (Exception ex)
{
Plugin.Instance.Error(ex);
}
}
internal static void CollectorPitch(AllocatedSource __instance, ref float value)
{
try
{
AudioRuntime? runtime = Plugin.Runtime;
if (runtime == null || !runtime.Ready || !Plugin.SfxEnabled.Value)
{
return;
}
object obj;
if (__instance == null)
{
obj = null;
}
else
{
PooledSource pooledSource = __instance.pooledSource;
obj = ((pooledSource != null) ? pooledSource.audioSource : null);
}
AudioSource val = (AudioSource)obj;
if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.clip))
{
string name = ((Object)val.clip).name;
if (name == "VillageAudio/coins_collect_01.ogg" || name == "VillageAudio/elixir_collect_02.ogg")
{
value = 1f;
}
}
}
catch (Exception ex)
{
Plugin.Instance.Error(ex);
}
}
internal static void Played(AudioSources __instance, Sound sound, AllocatedSource __result)
{
try
{
Plugin.Runtime?.CaptureMenuSource(__instance, sound, __result);
}
catch (Exception ex)
{
Plugin.Instance.Error(ex);
}
}
internal static void TransitionPrefix(Music __instance, Sound track, out float __state)
{
__state = __instance.transitionDuration;
AudioRuntime? runtime = Plugin.Runtime;
if (runtime == null || !runtime.Ready || !Plugin.MusicEnabled.Value || (!Plugin.Runtime.OwnsSound(track) && !Plugin.Runtime.OwnsSound(__instance.currentTrack)))
{
return;
}
if (Plugin.Fade.Value)
{
__instance.transitionDuration = Plugin.FadeSeconds.Value;
return;
}
__instance.currentSource.Stop();
List<AllocatedSource> dyingSources = __instance.dyingSources;
int num = 0;
while (dyingSources != null && num < dyingSources.Count)
{
dyingSources[num].Stop();
num++;
}
dyingSources?.Clear();
__instance.transitionDuration = 0.001f;
}
internal static void TransitionPostfix(Music __instance, Sound track, float __state)
{
Plugin.Runtime?.Played(__instance, track);
AudioRuntime? runtime = Plugin.Runtime;
__instance.transitionDuration = ((runtime == null || !runtime.Ready || !Plugin.MusicEnabled.Value || !Plugin.Runtime.OwnsSound(track)) ? (Plugin.Runtime?.OriginalDuration(__instance) ?? __state) : (Plugin.Fade.Value ? Plugin.FadeSeconds.Value : 0.001f));
}
}
}