Decompiled source of EscapeTheBackroomsHotelChaseMusic v1.0.0
icon/BepInEx/plugins/PizzaTowerEscapeMusic.dll
Decompiled 5 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PizzaTowerEscapeMusic.Scripting; using PizzaTowerEscapeMusic.Scripting.Conditions; using PizzaTowerEscapeMusic.Scripting.ScriptEvents; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PizzaTowerEscapeMusic")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Plays music from Pizza Tower when the early ship leave alert appears")] [assembly: AssemblyFileVersion("2.4.0.0")] [assembly: AssemblyInformationalVersion("2.4.0")] [assembly: AssemblyProduct("PizzaTowerEscapeMusic")] [assembly: AssemblyTitle("PizzaTowerEscapeMusic")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.4.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.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] [Microsoft.CodeAnalysis.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; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PizzaTowerEscapeMusic { public class Configuration { private readonly ConfigFile config; internal ConfigEntry<float> volumeMaster; internal ConfigEntry<string> scriptingScripts; public Configuration(ConfigFile config) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown this.config = config; scriptingScripts = config.Bind<string>("Scripting", "Scripts", "Default", new ConfigDescription("The names of the JSON script files that will be loaded (Separated by commas, do not put a space after the commas)", (AcceptableValueBase)null, Array.Empty<object>())); volumeMaster = config.Bind<float>("Volume", "Master", 0.5f, new ConfigDescription("The volume of the music as a whole, all volumes are scaled by this value", (AcceptableValueBase)null, Array.Empty<object>())); RemoveObsoleteEntries(); } private void RemoveObsoleteEntry(string section, string key) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown ConfigDefinition val = new ConfigDefinition(section, key); config.Bind<string>(val, "", (ConfigDescription)null); config.Remove(val); } private void ReplaceObsoleteEntry<T>(string section, string key, ConfigEntry<T> replacement) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown ConfigDefinition val = new ConfigDefinition(section, key); ConfigEntry<T> val2 = config.Bind<T>(val, (T)((ConfigEntryBase)replacement).DefaultValue, (ConfigDescription)null); if (!EqualityComparer<T>.Default.Equals(val2.Value, (T)((ConfigEntryBase)replacement).DefaultValue)) { replacement.Value = val2.Value; } config.Remove(val); } private void RemoveObsoleteEntries() { RemoveObsoleteEntry("Volume", "InsideFacility"); RemoveObsoleteEntry("Volume", "OutsideFacility"); RemoveObsoleteEntry("Volume", "InsideShip"); RemoveObsoleteEntry("Volume", "CrouchingScale"); RemoveObsoleteEntry("Music", "InsideFacility"); RemoveObsoleteEntry("Music", "OutsideFacility"); RemoveObsoleteEntry("Music", "HeavyWeather"); ReplaceObsoleteEntry<string>("Scripting", "Script", scriptingScripts); config.Save(); } } internal static class CustomManager { public static string GetFilePath(string path, string fallbackPath) { string[] directories = Directory.GetDirectories(Paths.PluginPath); for (int i = 0; i < directories.Length; i++) { string text = directories[i] + "/BGN-PizzaTowerEscapeMusic/" + path; if (File.Exists(text)) { return text; } } string text2 = Paths.PluginPath + "/BGN-PizzaTowerEscapeMusic_Custom/" + path; if (File.Exists(text2)) { return text2; } return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "/" + fallbackPath; } } public class GameEventListener : MonoBehaviour { private ManualLogSource logger; public Action OnFrameUpdate = delegate { }; public Action OnSoundManagerCreated = delegate { }; public Action OnSoundManagerDestroyed = delegate { }; public Action OnDungeonDoneGenerating = delegate { }; public Action OnShipLanded = delegate { }; public Action OnShipTakeOff = delegate { }; public Action OnShipReturnToOrbit = delegate { }; public Action OnShipLeavingAlertCalled = delegate { }; public Action OnPlayerDamaged = delegate { }; public Action OnPlayerDeath = delegate { }; public Action OnPlayerEnteredFacility = delegate { }; public Action OnPlayerExitedFacility = delegate { }; public Action OnPlayerEnteredShip = delegate { }; public Action OnPlayerExitedShip = delegate { }; public Action OnApparatusTaken = delegate { }; public Action<SelectableLevel?> OnCurrentMoonChanged = delegate { }; private readonly Dictionary<string, object?> previousValues = new Dictionary<string, object>(); private static LungProp? dockedApparatus; private void Awake() { logger = Logger.CreateLogSource("PizzaTowerEscapeMusic GameEventListener"); OnShipLanded = (Action)Delegate.Combine(OnShipLanded, new Action(FindDockedApparatus)); } private void FindDockedApparatus() { logger.LogDebug((object)"Checking for docked Apparatus..."); LungProp[] array = Object.FindObjectsOfType<LungProp>(); foreach (LungProp val in array) { if (val.isLungDocked) { logger.LogDebug((object)"Found docked Apparatus"); dockedApparatus = val; return; } } logger.LogDebug((object)"Could not find docked Apparatus"); } public static bool IsApparatusDocked() { return (Object)(object)dockedApparatus != (Object)null; } private void Update() { if ((Object)(object)SoundManager.Instance != (Object)null) { OnFrameUpdate(); } CheckSoundManager(); CheckDungeonDoneGenerating(); CheckShipLanded(); CheckShipReturnToOrbit(); CheckShipLeavingAlertCalled(); CheckPlayerDamaged(); CheckPlayerDeath(); CheckPlayerInsideFacility(); CheckPlayerInsideShip(); CheckApparatusTaken(); CheckCurrentMoonChanged(); } private T? UpdateCached<T>(string key, T? currentValue, T? defaultValue) { if (!previousValues.TryGetValue(key, out object value)) { value = defaultValue; } previousValues[key] = currentValue; return (T)value; } private void CheckSoundManager() { bool flag = (Object)(object)SoundManager.Instance != (Object)null; if (UpdateCached("SoundManager", flag, defaultValue: false) != flag) { if (flag) { logger.LogDebug((object)"Sound Manager created"); OnSoundManagerCreated(); } else { logger.LogDebug((object)"Sound Manager destroyed"); OnSoundManagerDestroyed(); } } } private void CheckDungeonDoneGenerating() { bool flag = (Object)(object)RoundManager.Instance != (Object)null && RoundManager.Instance.dungeonCompletedGenerating; bool flag2 = UpdateCached("DungeonDoneGenerating", flag, defaultValue: false); if (flag != flag2 && flag) { logger.LogDebug((object)"Dungeon done generating"); OnDungeonDoneGenerating(); } } private void CheckShipLanded() { bool flag = (Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.shipHasLanded; bool flag2 = UpdateCached("ShipLanded", flag, defaultValue: true); if (flag != flag2 && !((Object)(object)StartOfRound.Instance == (Object)null)) { if (flag) { logger.LogDebug((object)"Ship has landed"); OnShipLanded(); } else { logger.LogDebug((object)"Ship has taken off"); OnShipTakeOff(); } } } private void CheckShipReturnToOrbit() { bool flag = (Object)(object)StartOfRound.Instance == (Object)null || (!StartOfRound.Instance.shipHasLanded && !StartOfRound.Instance.shipIsLeaving); bool flag2 = UpdateCached("ShipReturnToOrbit", flag, defaultValue: true); if (flag != flag2 && flag) { logger.LogDebug((object)"Ship returned to orbit"); OnShipReturnToOrbit(); } } private void CheckShipLeavingAlertCalled() { bool flag = (Object)(object)TimeOfDay.Instance != (Object)null && TimeOfDay.Instance.shipLeavingAlertCalled; bool flag2 = UpdateCached("ShipLeavingAlertCalled", flag, defaultValue: false); if (flag != flag2 && flag) { logger.LogDebug((object)"Ship leaving alert called"); OnShipLeavingAlertCalled(); } } private void CheckPlayerDamaged() { if (!((Object)(object)GameNetworkManager.Instance == (Object)null) && !((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null)) { int health = GameNetworkManager.Instance.localPlayerController.health; int num = UpdateCached("PlayerDamaged", health, 100); if (health < num) { logger.LogDebug((object)$"Player took damage (Health: {GameNetworkManager.Instance.localPlayerController.health})"); OnPlayerDamaged(); } } } private void CheckPlayerDeath() { bool flag = (Object)(object)GameNetworkManager.Instance != (Object)null && (Object)(object)GameNetworkManager.Instance.localPlayerController != (Object)null && GameNetworkManager.Instance.localPlayerController.isPlayerDead; bool flag2 = UpdateCached("PlayerDeath", flag, defaultValue: false); if (flag != flag2 && flag) { logger.LogDebug((object)"Player has died"); OnPlayerDeath(); } } private void CheckPlayerInsideFacility() { bool flag = (Object)(object)GameNetworkManager.Instance != (Object)null && (Object)(object)GameNetworkManager.Instance.localPlayerController != (Object)null && GameNetworkManager.Instance.localPlayerController.isInsideFactory; bool flag2 = UpdateCached("PlayerInsideFacility", flag, defaultValue: false); if (flag != flag2) { if (flag) { logger.LogDebug((object)"Player entered facility"); OnPlayerEnteredFacility(); } else { logger.LogDebug((object)"Player exited facility"); OnPlayerExitedFacility(); } } } private void CheckPlayerInsideShip() { bool flag = (Object)(object)GameNetworkManager.Instance != (Object)null && (Object)(object)GameNetworkManager.Instance.localPlayerController != (Object)null && GameNetworkManager.Instance.localPlayerController.isInHangarShipRoom; bool flag2 = UpdateCached("PlayerInsideShip", flag, defaultValue: false); if (flag != flag2) { if (flag) { logger.LogDebug((object)"Player entered ship"); OnPlayerEnteredShip(); } else { logger.LogDebug((object)"Player exited ship"); OnPlayerExitedShip(); } } } private void CheckApparatusTaken() { bool flag = (Object)(object)dockedApparatus != (Object)null && !dockedApparatus.isLungDocked; bool flag2 = UpdateCached("ApparatusTaken", flag, defaultValue: false); if (flag != flag2 && flag) { dockedApparatus = null; logger.LogDebug((object)"Apparatus was taken"); OnApparatusTaken(); } } private void CheckCurrentMoonChanged() { SelectableLevel val = TimeOfDay.Instance?.currentLevel; SelectableLevel val2 = UpdateCached<SelectableLevel>("CurrentMoon", val, null); if (!((Object)(object)val == (Object)(object)val2)) { logger.LogDebug((object)("Level has changed to " + val?.PlanetName)); OnCurrentMoonChanged(val); } } } public class MusicManager : MonoBehaviour { private class MusicInstance { public Script script; public ScriptEvent_PlayMusic musicEvent; public AudioSource audioSource; public Script.VolumeGroup volumeGroup; private bool isStopping; private float volume; public float FadeSpeed { get; private set; } public MusicInstance(Script script, ScriptEvent_PlayMusic musicEvent, AudioSource audioSource, AudioClip? musicClip) { this.script = script; this.musicEvent = musicEvent; this.audioSource = audioSource; audioSource.clip = musicClip; audioSource.loop = musicEvent.loop; audioSource.Play(); musicInstances.Add(this); if (musicEvent.tag != null) { if (!musicInstancesByTag.TryGetValue(musicEvent.tag, out List<MusicInstance> value)) { value = new List<MusicInstance>(1); musicInstancesByTag.Add(musicEvent.tag, value); } value.Add(this); } volumeGroup = script.TryGetVolumeGroupOrDefault(musicEvent.tag); volume = volumeGroup.GetVolume(script); } public void Update(float deltaTime) { float num = (isStopping ? 0f : volumeGroup.GetVolume(script)); float num2 = (isStopping ? volumeGroup.stoppingVolumeLerpSpeed : volumeGroup.volumeLerpSpeed); volume = Mathf.Lerp(volume, num, num2 * deltaTime); audioSource.volume = volume * PizzaTowerEscapeMusicManager.Configuration.volumeMaster.Value; if (!audioSource.isPlaying || (isStopping && audioSource.volume < 0.005f)) { StopCompletely(); } } public void FadeStop() { if (!isStopping) { isStopping = true; FadeSpeed = volumeGroup.stoppingVolumeLerpSpeed; } } public void StopCompletely() { audioSource.Stop(); audioSourcePool.Push(audioSource); musicInstances.Remove(this); if (musicEvent.tag != null && musicInstancesByTag.TryGetValue(musicEvent.tag, out List<MusicInstance> value)) { value.Remove(this); } } } private ManualLogSource logger; private static readonly List<MusicInstance> musicInstances = new List<MusicInstance>(); private static readonly Dictionary<string, List<MusicInstance>> musicInstancesByTag = new Dictionary<string, List<MusicInstance>>(); private static readonly Stack<AudioSource> audioSourcePool = new Stack<AudioSource>(); private readonly Dictionary<string, AudioClip> loadedMusic = new Dictionary<string, AudioClip>(); private void Awake() { logger = Logger.CreateLogSource("PizzaTowerEscapeMusic MusicManager"); } private void Update() { if ((Object)(object)StartOfRound.Instance == (Object)null) { return; } for (int num = musicInstances.Count - 1; num >= 0; num--) { musicInstances[num].Update(Time.deltaTime); } bool flag = false; foreach (MusicInstance musicInstance in musicInstances) { if (musicInstance.musicEvent.silenceGameMusic) { flag = true; break; } } if (flag) { if (SoundManager.Instance.playingOutsideMusic && GetIsMusicPlaying()) { SoundManager.Instance.playingOutsideMusic = false; logger.LogInfo((object)"Silenced the outside music because alternate music is playing"); } if (TimeOfDay.Instance.TimeOfDayMusic.isPlaying && GetIsMusicPlaying()) { TimeOfDay.Instance.TimeOfDayMusic.Stop(); logger.LogInfo((object)"Silenced the time of day music because alternate music is playing"); } } } public bool GetIsMusicPlaying(string? tag = null) { if (tag == null) { return musicInstances.Count > 0; } if (musicInstancesByTag.TryGetValue(tag, out List<MusicInstance> value)) { logger.LogDebug((object)$"GetIsMusicPlaying says there's {value.Count} music instance(s) with the tag \"{tag}\""); return value.Count > 0; } logger.LogDebug((object)("GetIsMusicPlaying says there was no music instance list for tag \"" + tag + "\"")); return false; } public void PlayMusic(Script script, ScriptEvent_PlayMusic musicEvent) { logger.LogDebug((object)("PlayMusic called\nTag: " + musicEvent.tag + $"\nOverlap handling: {musicEvent.overlapHandling}" + $"\nAny music playing?: {GetIsMusicPlaying()}" + $"\nAny music playing with tag?: {GetIsMusicPlaying(musicEvent.tag)}")); if (musicEvent.overlapHandling == ScriptEvent_PlayMusic.OverlapHandling.IgnoreAll && GetIsMusicPlaying()) { logger.LogDebug((object)"PlayMusic canceled because other music was playing"); return; } if (musicEvent.overlapHandling == ScriptEvent_PlayMusic.OverlapHandling.IgnoreTag && GetIsMusicPlaying(musicEvent.tag)) { logger.LogDebug((object)("PlayMusic canceled because other music with the tag \"" + musicEvent.tag + "\" was playing")); return; } switch (musicEvent.overlapHandling) { case ScriptEvent_PlayMusic.OverlapHandling.OverrideAll: StopMusic(); break; case ScriptEvent_PlayMusic.OverlapHandling.OverrideTag: StopMusic(musicEvent.tag); break; case ScriptEvent_PlayMusic.OverlapHandling.OverrideFadeAll: FadeStopMusic(); break; case ScriptEvent_PlayMusic.OverlapHandling.OverrideFadeTag: FadeStopMusic(musicEvent.tag); break; } string text = musicEvent.musicNames[Random.Range(0, musicEvent.musicNames.Length)]; loadedMusic.TryGetValue(text, out AudioClip value); if ((Object)(object)value != (Object)null) { new MusicInstance(script, musicEvent, GetAudioSource(), value); logger.LogInfo((object)("Playing music (" + text + ")")); } else { logger.LogWarning((object)("Music (" + text + ") is null, cannot play. Maybe it wasn't loaded correctly?")); } } public void StopMusic(string? targetTag = null) { foreach (MusicInstance item in new List<MusicInstance>(musicInstances)) { if (targetTag == null || !(item.musicEvent.tag != targetTag)) { item.StopCompletely(); } } } public void FadeStopMusic(string? targetTag = null) { foreach (MusicInstance item in new List<MusicInstance>(musicInstances)) { if (targetTag == null || !(item.musicEvent.tag != targetTag)) { item.FadeStop(); } } } private AudioSource GetAudioSource() { if (!audioSourcePool.TryPop(out AudioSource result)) { return ((Component)this).gameObject.AddComponent<AudioSource>(); } return result; } public async void LoadNecessaryMusicClips() { if (PizzaTowerEscapeMusicManager.ScriptManager.loadedScripts.Count == 0) { logger.LogError((object)"No scripts are loaded, cannot load their music!"); return; } UnloadMusicClips(); foreach (Script loadedScript in PizzaTowerEscapeMusicManager.ScriptManager.loadedScripts) { ScriptEvent[] scriptEvents = loadedScript.scriptEvents; for (int i = 0; i < scriptEvents.Length; i++) { if (!(scriptEvents[i] is ScriptEvent_PlayMusic scriptEvent_PlayMusic)) { continue; } string[] musicNames = scriptEvent_PlayMusic.musicNames; foreach (string musicName in musicNames) { if (!loadedMusic.ContainsKey(musicName)) { AudioClip val = await LoadMusicClip(musicName); if (!((Object)(object)val == (Object)null)) { loadedMusic.Add(musicName, val); } } } } } logger.LogInfo((object)"Music clips done loading"); } public void UnloadMusicClips() { foreach (AudioClip value in loadedMusic.Values) { value.UnloadAudioData(); } loadedMusic.Clear(); logger.LogInfo((object)"All music clips unloaded"); } private async Task<AudioClip?> LoadMusicClip(string musicFileName) { InterpretMusicFileName(musicFileName, out AudioType audioType, out string finalFileName); string path = "file:///" + CustomManager.GetFilePath("Music/" + finalFileName, "DefaultMusic/" + finalFileName); UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(path, audioType); try { request.SendWebRequest(); while (!request.isDone) { await Task.Delay(50); } if ((int)request.result == 1) { logger.LogInfo((object)("Loaded music (" + musicFileName + ") from file")); AudioClip content = DownloadHandlerAudioClip.GetContent(request); ((Object)content).name = musicFileName; return content; } logger.LogError((object)($"Failed to load music ({musicFileName}) from file as audio type {audioType}, if the file extension and the audio type do not match the file extension may not be supported." + "\n- Path: " + path + "\n- Error: " + request.error)); return null; } finally { ((IDisposable)request)?.Dispose(); } } private void InterpretMusicFileName(string musicFileName, out AudioType audioType, out string finalFileName) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected I4, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) if (!musicFileName.Contains('.')) { audioType = (AudioType)20; finalFileName = musicFileName + ".wav"; return; } string text = musicFileName.Split('.').Last().ToLower(); AudioType val = ((text == "ogg") ? ((AudioType)14) : ((!(text == "mp3")) ? ((AudioType)20) : ((AudioType)13))); audioType = (AudioType)(int)val; finalFileName = musicFileName; } } public class PizzaTowerEscapeMusicManager : MonoBehaviour { private GameEventListener gameEventListener; private ManualLogSource logger; public static Configuration Configuration { get; private set; } public static ScriptManager ScriptManager { get; private set; } public static MusicManager MusicManager { get; private set; } public void Initialise(ManualLogSource logger, ConfigFile config) { this.logger = logger; Configuration = new Configuration(config); MusicManager = ((Component)this).gameObject.AddComponent<MusicManager>(); gameEventListener = ((Component)this).gameObject.AddComponent<GameEventListener>(); GameEventListener obj = gameEventListener; obj.OnSoundManagerCreated = (Action)Delegate.Combine(obj.OnSoundManagerCreated, new Action(MusicManager.LoadNecessaryMusicClips)); GameEventListener obj2 = gameEventListener; obj2.OnSoundManagerDestroyed = (Action)Delegate.Combine(obj2.OnSoundManagerDestroyed, (Action)delegate { MusicManager.StopMusic(); }); GameEventListener obj3 = gameEventListener; obj3.OnSoundManagerDestroyed = (Action)Delegate.Combine(obj3.OnSoundManagerDestroyed, new Action(MusicManager.UnloadMusicClips)); ScriptManager = new ScriptManager(Configuration.scriptingScripts.Value.Split(','), gameEventListener); GameEventListener obj4 = gameEventListener; obj4.OnSoundManagerDestroyed = (Action)Delegate.Combine(obj4.OnSoundManagerDestroyed, new Action(ScriptManager.ClearAllScriptTimers)); ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; logger.LogInfo((object)"Plugin bgn.pizzatowerescapemusic is loaded!"); } private void Update() { ScriptManager.UpdateAllScriptTimers(Time.deltaTime); } } [BepInPlugin("bgn.pizzatowerescapemusic", "PizzaTowerEscapeMusic", "2.4.0")] [BepInProcess("Lethal Company.exe")] public class Plugin : BaseUnityPlugin { public const string GUID = "bgn.pizzatowerescapemusic"; private void Awake() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("PizzaTowerEscapeMusic Manager"); val.AddComponent<PizzaTowerEscapeMusicManager>().Initialise(((BaseUnityPlugin)this).Logger, ((BaseUnityPlugin)this).Config); ((Object)val).hideFlags = (HideFlags)61; } } public static class PluginInfo { public const string PLUGIN_GUID = "PizzaTowerEscapeMusic"; public const string PLUGIN_NAME = "PizzaTowerEscapeMusic"; public const string PLUGIN_VERSION = "2.4.0"; } } namespace PizzaTowerEscapeMusic.Scripting { public class Script { public class VolumeRule { public string comment = string.Empty; [JsonRequired] public float volume; public Condition? condition; } public class VolumeModifier { public string comment = string.Empty; [JsonRequired] public float volumeScale; public Condition? condition; } public class VolumeGroup { public string comment = string.Empty; public string tag = string.Empty; public float volumeLerpSpeed = 1f; public float stoppingVolumeLerpSpeed = 1f; public float masterVolume = 1f; public VolumeRule[] volumeRules = Array.Empty<VolumeRule>(); public VolumeModifier[] volumeModifiers = Array.Empty<VolumeModifier>(); public float GetVolume(Script script) { float num = 1f; VolumeRule[] array = volumeRules; foreach (VolumeRule volumeRule in array) { if (volumeRule.condition == null || volumeRule.condition.Check(script)) { num = volumeRule.volume; break; } } VolumeModifier[] array2 = volumeModifiers; foreach (VolumeModifier volumeModifier in array2) { if (volumeModifier.condition == null || volumeModifier.condition.Check(script)) { num *= volumeModifier.volumeScale; } } return num * masterVolume; } } public class Timer { public string name; public float time; public Timer(string name) { this.name = name; } } public string comment = string.Empty; public bool isAddon; public VolumeGroup[] volumeGroups = Array.Empty<VolumeGroup>(); [JsonRequired] public ScriptEvent[] scriptEvents = Array.Empty<ScriptEvent>(); [JsonIgnore] public readonly Dictionary<ScriptEvent.GameEventType, List<ScriptEvent>> loadedScriptEvents = new Dictionary<ScriptEvent.GameEventType, List<ScriptEvent>>(); [JsonIgnore] public readonly Dictionary<string, VolumeGroup> loadedScriptVolumeGroups = new Dictionary<string, VolumeGroup>(); [JsonIgnore] public readonly Dictionary<string, Timer> activeTimers = new Dictionary<string, Timer>(); [JsonIgnore] public static VolumeGroup DefaultVolumeGroup { get; private set; } = new VolumeGroup(); public void Initialise(ManualLogSource logger) { ScriptEvent[] array = scriptEvents; foreach (ScriptEvent scriptEvent in array) { if (!loadedScriptEvents.TryGetValue(scriptEvent.gameEventType, out List<ScriptEvent> value)) { value = new List<ScriptEvent>(1); loadedScriptEvents.Add(scriptEvent.gameEventType, value); } value.Add(scriptEvent); } VolumeGroup[] array2 = volumeGroups; foreach (VolumeGroup volumeGroup in array2) { if (!loadedScriptVolumeGroups.TryAdd(volumeGroup.tag, volumeGroup)) { logger.LogError((object)("Volume group tag \"" + volumeGroup.tag + "\" was already declared, you cannot have two volume groups with the same tag")); } } } public VolumeGroup TryGetVolumeGroupOrDefault(string? tag) { if (tag == null || !loadedScriptVolumeGroups.ContainsKey(tag)) { return DefaultVolumeGroup; } return loadedScriptVolumeGroups[tag]; } public bool TryGetVolumeGroup(string? tag, [NotNullWhen(true)] out VolumeGroup? volumeGroup) { if (tag == null || !loadedScriptVolumeGroups.ContainsKey(tag)) { volumeGroup = null; return false; } volumeGroup = loadedScriptVolumeGroups[tag]; return true; } public void UpdateTimers(float deltaTime) { foreach (Timer value in activeTimers.Values) { value.time += deltaTime; } } public void ClearTimers() { activeTimers.Clear(); } internal void ClearTimer(string timerName) { activeTimers.Remove(timerName); } } public class ScriptManager { public readonly List<Script> loadedScripts = new List<Script>(); public ManualLogSource Logger { get; private set; } public ScriptManager(string[] scriptNames, GameEventListener gameEventListener) { Logger = Logger.CreateLogSource("PizzaTowerEscapeMusic ScriptManager"); Script script = new Script(); loadedScripts.Add(script); foreach (string text in scriptNames) { Script script2 = DeserializeScript(text); if (script2 != null) { script2.Initialise(Logger); if (script2.isAddon) { List<Script.VolumeGroup> list = script.volumeGroups.ToList(); list.AddRange(script2.volumeGroups); script.volumeGroups = list.ToArray(); List<ScriptEvent> list2 = script.scriptEvents.ToList(); list2.AddRange(script2.scriptEvents); script.scriptEvents = list2.ToArray(); } else { loadedScripts.Add(script2); } if (script2.isAddon) { Logger.LogInfo((object)("Script (" + text + ") loaded as addon")); } else { Logger.LogInfo((object)("Script (" + text + ") loaded")); } } } script.Initialise(Logger); gameEventListener.OnFrameUpdate = (Action)Delegate.Combine(gameEventListener.OnFrameUpdate, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.FrameUpdated); }); gameEventListener.OnShipLanded = (Action)Delegate.Combine(gameEventListener.OnShipLanded, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.ShipLanded); }); gameEventListener.OnShipTakeOff = (Action)Delegate.Combine(gameEventListener.OnShipTakeOff, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.ShipTakeOff); }); gameEventListener.OnShipLeavingAlertCalled = (Action)Delegate.Combine(gameEventListener.OnShipLeavingAlertCalled, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.ShipLeavingAlertCalled); }); gameEventListener.OnPlayerDamaged = (Action)Delegate.Combine(gameEventListener.OnPlayerDamaged, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.PlayerDamaged); }); gameEventListener.OnPlayerDeath = (Action)Delegate.Combine(gameEventListener.OnPlayerDeath, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.PlayerDied); }); gameEventListener.OnPlayerEnteredFacility = (Action)Delegate.Combine(gameEventListener.OnPlayerEnteredFacility, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.PlayerEnteredFacility); }); gameEventListener.OnPlayerExitedFacility = (Action)Delegate.Combine(gameEventListener.OnPlayerExitedFacility, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.PlayerExitedFacility); }); gameEventListener.OnPlayerEnteredShip = (Action)Delegate.Combine(gameEventListener.OnPlayerEnteredShip, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.PlayerEnteredShip); }); gameEventListener.OnPlayerExitedShip = (Action)Delegate.Combine(gameEventListener.OnPlayerExitedShip, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.PlayerExitedShip); }); gameEventListener.OnApparatusTaken = (Action)Delegate.Combine(gameEventListener.OnApparatusTaken, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.ApparatusTaken); }); gameEventListener.OnCurrentMoonChanged = (Action<SelectableLevel>)Delegate.Combine(gameEventListener.OnCurrentMoonChanged, (Action<SelectableLevel>)delegate { CheckScriptEvents(ScriptEvent.GameEventType.CurrentMoonChanged); }); Logger.LogInfo((object)"Done loading scripts"); } private void CheckScriptEvents(ScriptEvent.GameEventType eventType) { foreach (Script loadedScript in loadedScripts) { if (!loadedScript.loadedScriptEvents.TryGetValue(eventType, out List<ScriptEvent> value)) { continue; } foreach (ScriptEvent item in value) { if (item.CheckConditions(loadedScript)) { Logger.LogDebug((object)("Conditions for a script event have been met!\n Script Event Type: " + item.scriptEventType + $"\n Game Event Type: {item.gameEventType}" + "\n Comment: " + item.comment)); item.Run(loadedScript); } } } } private Script? DeserializeScript(string name) { string filePath = CustomManager.GetFilePath("Scripts/" + name + ".json", "DefaultScripts/" + name + ".json"); if (!File.Exists(filePath)) { Logger.LogError((object)("Script \"" + name + "\" does not exist! Make sure you spelt it right the config, and make sure its file extension is \".json\"")); return null; } string text = File.ReadAllText(filePath); try { return JsonConvert.DeserializeObject<Script>(text); } catch (Exception ex) { Logger.LogError((object)("Failed to deserialize script \"" + name + "\":\n" + ex.Message)); return null; } } public void UpdateAllScriptTimers(float deltaTime) { foreach (Script loadedScript in loadedScripts) { loadedScript.UpdateTimers(deltaTime); } } public void ClearAllScriptTimers() { foreach (Script loadedScript in loadedScripts) { loadedScript.ClearTimers(); } } } } namespace PizzaTowerEscapeMusic.Scripting.ScriptEvents { public class ScriptEventConverter : JsonConverter<ScriptEvent> { public override bool CanWrite => false; public override ScriptEvent ReadJson(JsonReader reader, Type objectType, ScriptEvent? existingValue, bool hasExistingValue, JsonSerializer serializer) { JObject val = JObject.Load(reader); JToken val2 = default(JToken); if (!val.TryGetValue("scriptEventType", ref val2)) { throw new Exception("scriptEventType type is null!"); } ScriptEvent scriptEvent = Extensions.Value<string>((IEnumerable<JToken>)val2) switch { "PlayMusic" => new ScriptEvent_PlayMusic(), "StopMusic" => new ScriptEvent_StopMusic(), "ResetTimers" => new ScriptEvent_ResetTimers(), "SetVolumeGroupMasterVolume" => new ScriptEvent_SetVolumeGroupMasterVolume(), _ => throw new Exception($"Condition type \"{val2}\" does not exist"), }; serializer.Populate(((JToken)val).CreateReader(), (object)scriptEvent); return scriptEvent; } public override void WriteJson(JsonWriter writer, ScriptEvent? value, JsonSerializer serializer) { throw new NotImplementedException(); } } [JsonConverter(typeof(ScriptEventConverter))] public abstract class ScriptEvent { public enum GameEventType { FrameUpdated, ShipLanded, ShipTakeOff, ShipLeavingAlertCalled, PlayerDamaged, PlayerDied, PlayerEnteredFacility, PlayerExitedFacility, PlayerEnteredShip, PlayerExitedShip, ApparatusTaken, CurrentMoonChanged } public string comment = string.Empty; [JsonRequired] public string scriptEventType = string.Empty; [JsonRequired] public GameEventType gameEventType; public Condition[] conditions = Array.Empty<Condition>(); public bool CheckConditions(Script script) { Script script2 = script; return !conditions.Any((Condition c) => !c.Check(script2)); } public abstract void Run(Script script); } public class ScriptEvent_PlayMusic : ScriptEvent { public enum OverlapHandling { IgnoreAll, IgnoreTag, OverrideAll, OverrideTag, OverrideFadeAll, OverrideFadeTag, Overlap } public bool loop; public bool silenceGameMusic = true; [JsonRequired] public OverlapHandling overlapHandling; public string? tag; [JsonRequired] public string[] musicNames = Array.Empty<string>(); public override void Run(Script script) { if (musicNames.Length != 0) { PizzaTowerEscapeMusicManager.MusicManager.PlayMusic(script, this); } } } public class ScriptEvent_StopMusic : ScriptEvent { public string[]? targetTags; public bool instant; public override void Run(Script script) { if (targetTags != null) { string[] array = targetTags; foreach (string targetTag in array) { if (instant) { PizzaTowerEscapeMusicManager.MusicManager.StopMusic(targetTag); } else { PizzaTowerEscapeMusicManager.MusicManager.FadeStopMusic(targetTag); } } } else if (instant) { PizzaTowerEscapeMusicManager.MusicManager.StopMusic(); } else { PizzaTowerEscapeMusicManager.MusicManager.FadeStopMusic(); } } } public class ScriptEvent_ResetTimers : ScriptEvent { public string[]? targetTimerNames; public override void Run(Script script) { if (targetTimerNames == null) { script.ClearTimers(); return; } string[] array = targetTimerNames; foreach (string timerName in array) { script.ClearTimer(timerName); } } } public class ScriptEvent_SetVolumeGroupMasterVolume : ScriptEvent { public string[] targetTags = Array.Empty<string>(); [JsonRequired] public float masterVolume; public override void Run(Script script) { if (targetTags.Length == 0) { Script.DefaultVolumeGroup.masterVolume = masterVolume; return; } string[] array = targetTags; foreach (string text in array) { if (!script.loadedScriptVolumeGroups.TryGetValue(text, out Script.VolumeGroup value)) { PizzaTowerEscapeMusicManager.ScriptManager.Logger.LogError((object)("Script Event SetVolumeGroupMasterVolume was called for volume group with tag \"" + text + "\", but there is no volume group of that tag")); } else { value.masterVolume = masterVolume; } } } } } namespace PizzaTowerEscapeMusic.Scripting.Conditions { public class ConditionConverter : JsonConverter<Condition> { public override bool CanWrite => false; public override Condition ReadJson(JsonReader reader, Type objectType, Condition? existingValue, bool hasExistingValue, JsonSerializer serializer) { JObject val = JObject.Load(reader); JToken val2 = default(JToken); if (!val.TryGetValue("conditionType", ref val2)) { throw new Exception("Condition type is null!"); } Condition condition = Extensions.Value<string>((IEnumerable<JToken>)val2) switch { "And" => new Condition_And(), "Or" => new Condition_Or(), "Not" => new Condition_Not(), "Weather" => new Condition_Weather(), "PlayerLocation" => new Condition_PlayerLocation(), "PlayerAlive" => new Condition_PlayerAlive(), "PlayerHealth" => new Condition_PlayerHealth(), "PlayerCrouching" => new Condition_PlayerCrouching(), "PlayerInsanity" => new Condition_PlayerInsanity(), "ShipLanded" => new Condition_ShipLanded(), "ShipLeavingAlertCalled" => new Condition_ShipLeavingAlertCalled(), "MusicWithTagPlaying" => new Condition_MusicWithTagPlaying(), "CurrentMoon" => new Condition_CurrentMoon(), "Timer" => new Condition_Timer(), "Random" => new Condition_Random(), "ApparatusDocked" => new Condition_ApparatusDocked(), "TimeOfDay" => new Condition_TimeOfDay(), _ => throw new Exception($"Condition type \"{val2}\" does not exist"), }; serializer.Populate(((JToken)val).CreateReader(), (object)condition); return condition; } public override void WriteJson(JsonWriter writer, Condition? value, JsonSerializer serializer) { throw new NotImplementedException(); } } [JsonConverter(typeof(ConditionConverter))] public abstract class Condition { [JsonRequired] public string conditionType = string.Empty; public abstract bool Check(Script script); } public abstract class ConditionComparableNumber : Condition { public enum ComparisonType { Equals, NotEquals, GreaterThan, LessThan, GreaterThanOrEquals, LessThanOrEquals } [JsonRequired] public ComparisonType comparisonType; } public class Condition_And : Condition { [JsonRequired] public Condition[] conditions = Array.Empty<Condition>(); public override bool Check(Script script) { Script script2 = script; return !conditions.Any((Condition c) => !c.Check(script2)); } } public class Condition_Or : Condition { [JsonRequired] public Condition[] conditions = Array.Empty<Condition>(); public override bool Check(Script script) { Script script2 = script; return conditions.Any((Condition c) => c.Check(script2)); } } public class Condition_Not : Condition { [JsonRequired] public Condition? condition; public override bool Check(Script script) { if (condition == null) { return true; } return !condition.Check(script); } } public class Condition_Weather : Condition { [JsonRequired] public LevelWeatherType weather; public override bool Check(Script script) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)TimeOfDay.Instance == (Object)null) { return false; } return TimeOfDay.Instance.currentLevelWeather == weather; } } public class Condition_PlayerLocation : Condition { public enum Location { Ship, Facility } [JsonRequired] public Location location; public override bool Check(Script script) { if ((Object)(object)GameNetworkManager.Instance == (Object)null) { return false; } if ((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) { return false; } return location switch { Location.Ship => GameNetworkManager.Instance.localPlayerController.isInHangarShipRoom, Location.Facility => GameNetworkManager.Instance.localPlayerController.isInsideFactory, _ => false, }; } } public class Condition_PlayerAlive : Condition { public override bool Check(Script script) { if ((Object)(object)GameNetworkManager.Instance == (Object)null) { return false; } if ((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) { return false; } return !GameNetworkManager.Instance.localPlayerController.isPlayerDead; } } public class Condition_PlayerHealth : ConditionComparableNumber { [JsonRequired] public int value; public override bool Check(Script script) { if ((Object)(object)GameNetworkManager.Instance == (Object)null) { return false; } if ((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) { return false; } int health = GameNetworkManager.Instance.localPlayerController.health; return comparisonType switch { ComparisonType.Equals => health == value, ComparisonType.NotEquals => health != value, ComparisonType.GreaterThan => health > value, ComparisonType.LessThan => health < value, ComparisonType.GreaterThanOrEquals => health >= value, ComparisonType.LessThanOrEquals => health <= value, _ => false, }; } } public class Condition_PlayerCrouching : Condition { public override bool Check(Script script) { if ((Object)(object)GameNetworkManager.Instance == (Object)null) { return false; } if ((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) { return false; } return GameNetworkManager.Instance.localPlayerController.isCrouching; } } public class Condition_PlayerInsanity : ConditionComparableNumber { [JsonRequired] public float level; public override bool Check(Script script) { if ((Object)(object)GameNetworkManager.Instance == (Object)null) { return false; } if ((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) { return false; } float num = GameNetworkManager.Instance.localPlayerController.insanityLevel / GameNetworkManager.Instance.localPlayerController.maxInsanityLevel; return comparisonType switch { ComparisonType.Equals => level == num, ComparisonType.NotEquals => level != num, ComparisonType.GreaterThan => level > num, ComparisonType.LessThan => level < num, ComparisonType.GreaterThanOrEquals => level >= num, ComparisonType.LessThanOrEquals => level <= num, _ => false, }; } } public class Condition_ShipLanded : Condition { public override bool Check(Script script) { if ((Object)(object)StartOfRound.Instance == (Object)null) { return false; } return StartOfRound.Instance.shipHasLanded; } } public class Condition_ShipLeavingAlertCalled : Condition { public override bool Check(Script script) { if ((Object)(object)TimeOfDay.Instance == (Object)null) { return false; } return TimeOfDay.Instance.shipLeavingAlertCalled; } } public class Condition_MusicWithTagPlaying : Condition { [JsonRequired] public string tag = string.Empty; public override bool Check(Script script) { return PizzaTowerEscapeMusicManager.MusicManager.GetIsMusicPlaying(tag); } } public class Condition_CurrentMoon : Condition { private static readonly Dictionary<string, int> moonNameToId = new Dictionary<string, int>(); [JsonRequired] public string moonName = string.Empty; private bool isDisabled; public override bool Check(Script script) { if ((Object)(object)TimeOfDay.Instance == (Object)null) { return false; } if ((Object)(object)StartOfRound.Instance == (Object)null) { return false; } if (isDisabled) { return false; } if (!moonNameToId.TryGetValue(moonName, out var value)) { SelectableLevel[] levels = StartOfRound.Instance.levels; foreach (SelectableLevel val in levels) { moonNameToId.TryAdd(val.PlanetName, val.levelID); } if (!moonNameToId.TryGetValue(moonName, out value)) { PizzaTowerEscapeMusicManager.ScriptManager.Logger.LogError((object)("From CurrentMoon condition: Found no existing level with the name \"" + moonName + "\"")); isDisabled = true; return false; } } return TimeOfDay.Instance.currentLevel.levelID == value; } } public class Condition_Timer : Condition { [JsonRequired] public string timerName = string.Empty; [JsonRequired] public float timeGoal; public bool resetsTimer = true; public override bool Check(Script script) { if (!script.activeTimers.TryGetValue(timerName, out Script.Timer value)) { value = new Script.Timer(timerName); script.activeTimers.Add(timerName, value); } if (value.time >= timeGoal) { if (resetsTimer) { value.time = 0f; } return true; } return false; } } public class Condition_Random : Condition { [JsonRequired] public float chance; public override bool Check(Script script) { return Random.Range(0f, 1f) <= chance; } } public class Condition_ApparatusDocked : Condition { public override bool Check(Script script) { return GameEventListener.IsApparatusDocked(); } } public class Condition_TimeOfDay : ConditionComparableNumber { [JsonRequired] public float time; public override bool Check(Script script) { if ((Object)(object)TimeOfDay.Instance == (Object)null) { return false; } float num = TimeOfDay.Instance.currentDayTime / TimeOfDay.Instance.totalTime; return comparisonType switch { ComparisonType.Equals => num == time, ComparisonType.NotEquals => num != time, ComparisonType.GreaterThan => num > time, ComparisonType.LessThan => num < time, ComparisonType.GreaterThanOrEquals => num >= time, ComparisonType.LessThanOrEquals => num <= time, _ => false, }; } } }
BepInEx/plugins/PizzaTowerEscapeMusic.dll
Decompiled 5 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PizzaTowerEscapeMusic.Scripting; using PizzaTowerEscapeMusic.Scripting.Conditions; using PizzaTowerEscapeMusic.Scripting.ScriptEvents; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PizzaTowerEscapeMusic")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Plays music from Pizza Tower when the early ship leave alert appears")] [assembly: AssemblyFileVersion("2.4.0.0")] [assembly: AssemblyInformationalVersion("2.4.0")] [assembly: AssemblyProduct("PizzaTowerEscapeMusic")] [assembly: AssemblyTitle("PizzaTowerEscapeMusic")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.4.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.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] [Microsoft.CodeAnalysis.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; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PizzaTowerEscapeMusic { public class Configuration { private readonly ConfigFile config; internal ConfigEntry<float> volumeMaster; internal ConfigEntry<string> scriptingScripts; public Configuration(ConfigFile config) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown this.config = config; scriptingScripts = config.Bind<string>("Scripting", "Scripts", "Default", new ConfigDescription("The names of the JSON script files that will be loaded (Separated by commas, do not put a space after the commas)", (AcceptableValueBase)null, Array.Empty<object>())); volumeMaster = config.Bind<float>("Volume", "Master", 0.5f, new ConfigDescription("The volume of the music as a whole, all volumes are scaled by this value", (AcceptableValueBase)null, Array.Empty<object>())); RemoveObsoleteEntries(); } private void RemoveObsoleteEntry(string section, string key) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown ConfigDefinition val = new ConfigDefinition(section, key); config.Bind<string>(val, "", (ConfigDescription)null); config.Remove(val); } private void ReplaceObsoleteEntry<T>(string section, string key, ConfigEntry<T> replacement) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown ConfigDefinition val = new ConfigDefinition(section, key); ConfigEntry<T> val2 = config.Bind<T>(val, (T)((ConfigEntryBase)replacement).DefaultValue, (ConfigDescription)null); if (!EqualityComparer<T>.Default.Equals(val2.Value, (T)((ConfigEntryBase)replacement).DefaultValue)) { replacement.Value = val2.Value; } config.Remove(val); } private void RemoveObsoleteEntries() { RemoveObsoleteEntry("Volume", "InsideFacility"); RemoveObsoleteEntry("Volume", "OutsideFacility"); RemoveObsoleteEntry("Volume", "InsideShip"); RemoveObsoleteEntry("Volume", "CrouchingScale"); RemoveObsoleteEntry("Music", "InsideFacility"); RemoveObsoleteEntry("Music", "OutsideFacility"); RemoveObsoleteEntry("Music", "HeavyWeather"); ReplaceObsoleteEntry<string>("Scripting", "Script", scriptingScripts); config.Save(); } } internal static class CustomManager { public static string GetFilePath(string path, string fallbackPath) { string[] directories = Directory.GetDirectories(Paths.PluginPath); for (int i = 0; i < directories.Length; i++) { string text = directories[i] + "/BGN-PizzaTowerEscapeMusic/" + path; if (File.Exists(text)) { return text; } } string text2 = Paths.PluginPath + "/BGN-PizzaTowerEscapeMusic_Custom/" + path; if (File.Exists(text2)) { return text2; } return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "/" + fallbackPath; } } public class GameEventListener : MonoBehaviour { private ManualLogSource logger; public Action OnFrameUpdate = delegate { }; public Action OnSoundManagerCreated = delegate { }; public Action OnSoundManagerDestroyed = delegate { }; public Action OnDungeonDoneGenerating = delegate { }; public Action OnShipLanded = delegate { }; public Action OnShipTakeOff = delegate { }; public Action OnShipReturnToOrbit = delegate { }; public Action OnShipLeavingAlertCalled = delegate { }; public Action OnPlayerDamaged = delegate { }; public Action OnPlayerDeath = delegate { }; public Action OnPlayerEnteredFacility = delegate { }; public Action OnPlayerExitedFacility = delegate { }; public Action OnPlayerEnteredShip = delegate { }; public Action OnPlayerExitedShip = delegate { }; public Action OnApparatusTaken = delegate { }; public Action<SelectableLevel?> OnCurrentMoonChanged = delegate { }; private readonly Dictionary<string, object?> previousValues = new Dictionary<string, object>(); private static LungProp? dockedApparatus; private void Awake() { logger = Logger.CreateLogSource("PizzaTowerEscapeMusic GameEventListener"); OnShipLanded = (Action)Delegate.Combine(OnShipLanded, new Action(FindDockedApparatus)); } private void FindDockedApparatus() { logger.LogDebug((object)"Checking for docked Apparatus..."); LungProp[] array = Object.FindObjectsOfType<LungProp>(); foreach (LungProp val in array) { if (val.isLungDocked) { logger.LogDebug((object)"Found docked Apparatus"); dockedApparatus = val; return; } } logger.LogDebug((object)"Could not find docked Apparatus"); } public static bool IsApparatusDocked() { return (Object)(object)dockedApparatus != (Object)null; } private void Update() { if ((Object)(object)SoundManager.Instance != (Object)null) { OnFrameUpdate(); } CheckSoundManager(); CheckDungeonDoneGenerating(); CheckShipLanded(); CheckShipReturnToOrbit(); CheckShipLeavingAlertCalled(); CheckPlayerDamaged(); CheckPlayerDeath(); CheckPlayerInsideFacility(); CheckPlayerInsideShip(); CheckApparatusTaken(); CheckCurrentMoonChanged(); } private T? UpdateCached<T>(string key, T? currentValue, T? defaultValue) { if (!previousValues.TryGetValue(key, out object value)) { value = defaultValue; } previousValues[key] = currentValue; return (T)value; } private void CheckSoundManager() { bool flag = (Object)(object)SoundManager.Instance != (Object)null; if (UpdateCached("SoundManager", flag, defaultValue: false) != flag) { if (flag) { logger.LogDebug((object)"Sound Manager created"); OnSoundManagerCreated(); } else { logger.LogDebug((object)"Sound Manager destroyed"); OnSoundManagerDestroyed(); } } } private void CheckDungeonDoneGenerating() { bool flag = (Object)(object)RoundManager.Instance != (Object)null && RoundManager.Instance.dungeonCompletedGenerating; bool flag2 = UpdateCached("DungeonDoneGenerating", flag, defaultValue: false); if (flag != flag2 && flag) { logger.LogDebug((object)"Dungeon done generating"); OnDungeonDoneGenerating(); } } private void CheckShipLanded() { bool flag = (Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.shipHasLanded; bool flag2 = UpdateCached("ShipLanded", flag, defaultValue: true); if (flag != flag2 && !((Object)(object)StartOfRound.Instance == (Object)null)) { if (flag) { logger.LogDebug((object)"Ship has landed"); OnShipLanded(); } else { logger.LogDebug((object)"Ship has taken off"); OnShipTakeOff(); } } } private void CheckShipReturnToOrbit() { bool flag = (Object)(object)StartOfRound.Instance == (Object)null || (!StartOfRound.Instance.shipHasLanded && !StartOfRound.Instance.shipIsLeaving); bool flag2 = UpdateCached("ShipReturnToOrbit", flag, defaultValue: true); if (flag != flag2 && flag) { logger.LogDebug((object)"Ship returned to orbit"); OnShipReturnToOrbit(); } } private void CheckShipLeavingAlertCalled() { bool flag = (Object)(object)TimeOfDay.Instance != (Object)null && TimeOfDay.Instance.shipLeavingAlertCalled; bool flag2 = UpdateCached("ShipLeavingAlertCalled", flag, defaultValue: false); if (flag != flag2 && flag) { logger.LogDebug((object)"Ship leaving alert called"); OnShipLeavingAlertCalled(); } } private void CheckPlayerDamaged() { if (!((Object)(object)GameNetworkManager.Instance == (Object)null) && !((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null)) { int health = GameNetworkManager.Instance.localPlayerController.health; int num = UpdateCached("PlayerDamaged", health, 100); if (health < num) { logger.LogDebug((object)$"Player took damage (Health: {GameNetworkManager.Instance.localPlayerController.health})"); OnPlayerDamaged(); } } } private void CheckPlayerDeath() { bool flag = (Object)(object)GameNetworkManager.Instance != (Object)null && (Object)(object)GameNetworkManager.Instance.localPlayerController != (Object)null && GameNetworkManager.Instance.localPlayerController.isPlayerDead; bool flag2 = UpdateCached("PlayerDeath", flag, defaultValue: false); if (flag != flag2 && flag) { logger.LogDebug((object)"Player has died"); OnPlayerDeath(); } } private void CheckPlayerInsideFacility() { bool flag = (Object)(object)GameNetworkManager.Instance != (Object)null && (Object)(object)GameNetworkManager.Instance.localPlayerController != (Object)null && GameNetworkManager.Instance.localPlayerController.isInsideFactory; bool flag2 = UpdateCached("PlayerInsideFacility", flag, defaultValue: false); if (flag != flag2) { if (flag) { logger.LogDebug((object)"Player entered facility"); OnPlayerEnteredFacility(); } else { logger.LogDebug((object)"Player exited facility"); OnPlayerExitedFacility(); } } } private void CheckPlayerInsideShip() { bool flag = (Object)(object)GameNetworkManager.Instance != (Object)null && (Object)(object)GameNetworkManager.Instance.localPlayerController != (Object)null && GameNetworkManager.Instance.localPlayerController.isInHangarShipRoom; bool flag2 = UpdateCached("PlayerInsideShip", flag, defaultValue: false); if (flag != flag2) { if (flag) { logger.LogDebug((object)"Player entered ship"); OnPlayerEnteredShip(); } else { logger.LogDebug((object)"Player exited ship"); OnPlayerExitedShip(); } } } private void CheckApparatusTaken() { bool flag = (Object)(object)dockedApparatus != (Object)null && !dockedApparatus.isLungDocked; bool flag2 = UpdateCached("ApparatusTaken", flag, defaultValue: false); if (flag != flag2 && flag) { dockedApparatus = null; logger.LogDebug((object)"Apparatus was taken"); OnApparatusTaken(); } } private void CheckCurrentMoonChanged() { SelectableLevel val = TimeOfDay.Instance?.currentLevel; SelectableLevel val2 = UpdateCached<SelectableLevel>("CurrentMoon", val, null); if (!((Object)(object)val == (Object)(object)val2)) { logger.LogDebug((object)("Level has changed to " + val?.PlanetName)); OnCurrentMoonChanged(val); } } } public class MusicManager : MonoBehaviour { private class MusicInstance { public Script script; public ScriptEvent_PlayMusic musicEvent; public AudioSource audioSource; public Script.VolumeGroup volumeGroup; private bool isStopping; private float volume; public float FadeSpeed { get; private set; } public MusicInstance(Script script, ScriptEvent_PlayMusic musicEvent, AudioSource audioSource, AudioClip? musicClip) { this.script = script; this.musicEvent = musicEvent; this.audioSource = audioSource; audioSource.clip = musicClip; audioSource.loop = musicEvent.loop; audioSource.Play(); musicInstances.Add(this); if (musicEvent.tag != null) { if (!musicInstancesByTag.TryGetValue(musicEvent.tag, out List<MusicInstance> value)) { value = new List<MusicInstance>(1); musicInstancesByTag.Add(musicEvent.tag, value); } value.Add(this); } volumeGroup = script.TryGetVolumeGroupOrDefault(musicEvent.tag); volume = volumeGroup.GetVolume(script); } public void Update(float deltaTime) { float num = (isStopping ? 0f : volumeGroup.GetVolume(script)); float num2 = (isStopping ? volumeGroup.stoppingVolumeLerpSpeed : volumeGroup.volumeLerpSpeed); volume = Mathf.Lerp(volume, num, num2 * deltaTime); audioSource.volume = volume * PizzaTowerEscapeMusicManager.Configuration.volumeMaster.Value; if (!audioSource.isPlaying || (isStopping && audioSource.volume < 0.005f)) { StopCompletely(); } } public void FadeStop() { if (!isStopping) { isStopping = true; FadeSpeed = volumeGroup.stoppingVolumeLerpSpeed; } } public void StopCompletely() { audioSource.Stop(); audioSourcePool.Push(audioSource); musicInstances.Remove(this); if (musicEvent.tag != null && musicInstancesByTag.TryGetValue(musicEvent.tag, out List<MusicInstance> value)) { value.Remove(this); } } } private ManualLogSource logger; private static readonly List<MusicInstance> musicInstances = new List<MusicInstance>(); private static readonly Dictionary<string, List<MusicInstance>> musicInstancesByTag = new Dictionary<string, List<MusicInstance>>(); private static readonly Stack<AudioSource> audioSourcePool = new Stack<AudioSource>(); private readonly Dictionary<string, AudioClip> loadedMusic = new Dictionary<string, AudioClip>(); private void Awake() { logger = Logger.CreateLogSource("PizzaTowerEscapeMusic MusicManager"); } private void Update() { if ((Object)(object)StartOfRound.Instance == (Object)null) { return; } for (int num = musicInstances.Count - 1; num >= 0; num--) { musicInstances[num].Update(Time.deltaTime); } bool flag = false; foreach (MusicInstance musicInstance in musicInstances) { if (musicInstance.musicEvent.silenceGameMusic) { flag = true; break; } } if (flag) { if (SoundManager.Instance.playingOutsideMusic && GetIsMusicPlaying()) { SoundManager.Instance.playingOutsideMusic = false; logger.LogInfo((object)"Silenced the outside music because alternate music is playing"); } if (TimeOfDay.Instance.TimeOfDayMusic.isPlaying && GetIsMusicPlaying()) { TimeOfDay.Instance.TimeOfDayMusic.Stop(); logger.LogInfo((object)"Silenced the time of day music because alternate music is playing"); } } } public bool GetIsMusicPlaying(string? tag = null) { if (tag == null) { return musicInstances.Count > 0; } if (musicInstancesByTag.TryGetValue(tag, out List<MusicInstance> value)) { logger.LogDebug((object)$"GetIsMusicPlaying says there's {value.Count} music instance(s) with the tag \"{tag}\""); return value.Count > 0; } logger.LogDebug((object)("GetIsMusicPlaying says there was no music instance list for tag \"" + tag + "\"")); return false; } public void PlayMusic(Script script, ScriptEvent_PlayMusic musicEvent) { logger.LogDebug((object)("PlayMusic called\nTag: " + musicEvent.tag + $"\nOverlap handling: {musicEvent.overlapHandling}" + $"\nAny music playing?: {GetIsMusicPlaying()}" + $"\nAny music playing with tag?: {GetIsMusicPlaying(musicEvent.tag)}")); if (musicEvent.overlapHandling == ScriptEvent_PlayMusic.OverlapHandling.IgnoreAll && GetIsMusicPlaying()) { logger.LogDebug((object)"PlayMusic canceled because other music was playing"); return; } if (musicEvent.overlapHandling == ScriptEvent_PlayMusic.OverlapHandling.IgnoreTag && GetIsMusicPlaying(musicEvent.tag)) { logger.LogDebug((object)("PlayMusic canceled because other music with the tag \"" + musicEvent.tag + "\" was playing")); return; } switch (musicEvent.overlapHandling) { case ScriptEvent_PlayMusic.OverlapHandling.OverrideAll: StopMusic(); break; case ScriptEvent_PlayMusic.OverlapHandling.OverrideTag: StopMusic(musicEvent.tag); break; case ScriptEvent_PlayMusic.OverlapHandling.OverrideFadeAll: FadeStopMusic(); break; case ScriptEvent_PlayMusic.OverlapHandling.OverrideFadeTag: FadeStopMusic(musicEvent.tag); break; } string text = musicEvent.musicNames[Random.Range(0, musicEvent.musicNames.Length)]; loadedMusic.TryGetValue(text, out AudioClip value); if ((Object)(object)value != (Object)null) { new MusicInstance(script, musicEvent, GetAudioSource(), value); logger.LogInfo((object)("Playing music (" + text + ")")); } else { logger.LogWarning((object)("Music (" + text + ") is null, cannot play. Maybe it wasn't loaded correctly?")); } } public void StopMusic(string? targetTag = null) { foreach (MusicInstance item in new List<MusicInstance>(musicInstances)) { if (targetTag == null || !(item.musicEvent.tag != targetTag)) { item.StopCompletely(); } } } public void FadeStopMusic(string? targetTag = null) { foreach (MusicInstance item in new List<MusicInstance>(musicInstances)) { if (targetTag == null || !(item.musicEvent.tag != targetTag)) { item.FadeStop(); } } } private AudioSource GetAudioSource() { if (!audioSourcePool.TryPop(out AudioSource result)) { return ((Component)this).gameObject.AddComponent<AudioSource>(); } return result; } public async void LoadNecessaryMusicClips() { if (PizzaTowerEscapeMusicManager.ScriptManager.loadedScripts.Count == 0) { logger.LogError((object)"No scripts are loaded, cannot load their music!"); return; } UnloadMusicClips(); foreach (Script loadedScript in PizzaTowerEscapeMusicManager.ScriptManager.loadedScripts) { ScriptEvent[] scriptEvents = loadedScript.scriptEvents; for (int i = 0; i < scriptEvents.Length; i++) { if (!(scriptEvents[i] is ScriptEvent_PlayMusic scriptEvent_PlayMusic)) { continue; } string[] musicNames = scriptEvent_PlayMusic.musicNames; foreach (string musicName in musicNames) { if (!loadedMusic.ContainsKey(musicName)) { AudioClip val = await LoadMusicClip(musicName); if (!((Object)(object)val == (Object)null)) { loadedMusic.Add(musicName, val); } } } } } logger.LogInfo((object)"Music clips done loading"); } public void UnloadMusicClips() { foreach (AudioClip value in loadedMusic.Values) { value.UnloadAudioData(); } loadedMusic.Clear(); logger.LogInfo((object)"All music clips unloaded"); } private async Task<AudioClip?> LoadMusicClip(string musicFileName) { InterpretMusicFileName(musicFileName, out AudioType audioType, out string finalFileName); string path = "file:///" + CustomManager.GetFilePath("Music/" + finalFileName, "DefaultMusic/" + finalFileName); UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(path, audioType); try { request.SendWebRequest(); while (!request.isDone) { await Task.Delay(50); } if ((int)request.result == 1) { logger.LogInfo((object)("Loaded music (" + musicFileName + ") from file")); AudioClip content = DownloadHandlerAudioClip.GetContent(request); ((Object)content).name = musicFileName; return content; } logger.LogError((object)($"Failed to load music ({musicFileName}) from file as audio type {audioType}, if the file extension and the audio type do not match the file extension may not be supported." + "\n- Path: " + path + "\n- Error: " + request.error)); return null; } finally { ((IDisposable)request)?.Dispose(); } } private void InterpretMusicFileName(string musicFileName, out AudioType audioType, out string finalFileName) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected I4, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) if (!musicFileName.Contains('.')) { audioType = (AudioType)20; finalFileName = musicFileName + ".wav"; return; } string text = musicFileName.Split('.').Last().ToLower(); AudioType val = ((text == "ogg") ? ((AudioType)14) : ((!(text == "mp3")) ? ((AudioType)20) : ((AudioType)13))); audioType = (AudioType)(int)val; finalFileName = musicFileName; } } public class PizzaTowerEscapeMusicManager : MonoBehaviour { private GameEventListener gameEventListener; private ManualLogSource logger; public static Configuration Configuration { get; private set; } public static ScriptManager ScriptManager { get; private set; } public static MusicManager MusicManager { get; private set; } public void Initialise(ManualLogSource logger, ConfigFile config) { this.logger = logger; Configuration = new Configuration(config); MusicManager = ((Component)this).gameObject.AddComponent<MusicManager>(); gameEventListener = ((Component)this).gameObject.AddComponent<GameEventListener>(); GameEventListener obj = gameEventListener; obj.OnSoundManagerCreated = (Action)Delegate.Combine(obj.OnSoundManagerCreated, new Action(MusicManager.LoadNecessaryMusicClips)); GameEventListener obj2 = gameEventListener; obj2.OnSoundManagerDestroyed = (Action)Delegate.Combine(obj2.OnSoundManagerDestroyed, (Action)delegate { MusicManager.StopMusic(); }); GameEventListener obj3 = gameEventListener; obj3.OnSoundManagerDestroyed = (Action)Delegate.Combine(obj3.OnSoundManagerDestroyed, new Action(MusicManager.UnloadMusicClips)); ScriptManager = new ScriptManager(Configuration.scriptingScripts.Value.Split(','), gameEventListener); GameEventListener obj4 = gameEventListener; obj4.OnSoundManagerDestroyed = (Action)Delegate.Combine(obj4.OnSoundManagerDestroyed, new Action(ScriptManager.ClearAllScriptTimers)); ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; logger.LogInfo((object)"Plugin bgn.pizzatowerescapemusic is loaded!"); } private void Update() { ScriptManager.UpdateAllScriptTimers(Time.deltaTime); } } [BepInPlugin("bgn.pizzatowerescapemusic", "PizzaTowerEscapeMusic", "2.4.0")] [BepInProcess("Lethal Company.exe")] public class Plugin : BaseUnityPlugin { public const string GUID = "bgn.pizzatowerescapemusic"; private void Awake() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("PizzaTowerEscapeMusic Manager"); val.AddComponent<PizzaTowerEscapeMusicManager>().Initialise(((BaseUnityPlugin)this).Logger, ((BaseUnityPlugin)this).Config); ((Object)val).hideFlags = (HideFlags)61; } } public static class PluginInfo { public const string PLUGIN_GUID = "PizzaTowerEscapeMusic"; public const string PLUGIN_NAME = "PizzaTowerEscapeMusic"; public const string PLUGIN_VERSION = "2.4.0"; } } namespace PizzaTowerEscapeMusic.Scripting { public class Script { public class VolumeRule { public string comment = string.Empty; [JsonRequired] public float volume; public Condition? condition; } public class VolumeModifier { public string comment = string.Empty; [JsonRequired] public float volumeScale; public Condition? condition; } public class VolumeGroup { public string comment = string.Empty; public string tag = string.Empty; public float volumeLerpSpeed = 1f; public float stoppingVolumeLerpSpeed = 1f; public float masterVolume = 1f; public VolumeRule[] volumeRules = Array.Empty<VolumeRule>(); public VolumeModifier[] volumeModifiers = Array.Empty<VolumeModifier>(); public float GetVolume(Script script) { float num = 1f; VolumeRule[] array = volumeRules; foreach (VolumeRule volumeRule in array) { if (volumeRule.condition == null || volumeRule.condition.Check(script)) { num = volumeRule.volume; break; } } VolumeModifier[] array2 = volumeModifiers; foreach (VolumeModifier volumeModifier in array2) { if (volumeModifier.condition == null || volumeModifier.condition.Check(script)) { num *= volumeModifier.volumeScale; } } return num * masterVolume; } } public class Timer { public string name; public float time; public Timer(string name) { this.name = name; } } public string comment = string.Empty; public bool isAddon; public VolumeGroup[] volumeGroups = Array.Empty<VolumeGroup>(); [JsonRequired] public ScriptEvent[] scriptEvents = Array.Empty<ScriptEvent>(); [JsonIgnore] public readonly Dictionary<ScriptEvent.GameEventType, List<ScriptEvent>> loadedScriptEvents = new Dictionary<ScriptEvent.GameEventType, List<ScriptEvent>>(); [JsonIgnore] public readonly Dictionary<string, VolumeGroup> loadedScriptVolumeGroups = new Dictionary<string, VolumeGroup>(); [JsonIgnore] public readonly Dictionary<string, Timer> activeTimers = new Dictionary<string, Timer>(); [JsonIgnore] public static VolumeGroup DefaultVolumeGroup { get; private set; } = new VolumeGroup(); public void Initialise(ManualLogSource logger) { ScriptEvent[] array = scriptEvents; foreach (ScriptEvent scriptEvent in array) { if (!loadedScriptEvents.TryGetValue(scriptEvent.gameEventType, out List<ScriptEvent> value)) { value = new List<ScriptEvent>(1); loadedScriptEvents.Add(scriptEvent.gameEventType, value); } value.Add(scriptEvent); } VolumeGroup[] array2 = volumeGroups; foreach (VolumeGroup volumeGroup in array2) { if (!loadedScriptVolumeGroups.TryAdd(volumeGroup.tag, volumeGroup)) { logger.LogError((object)("Volume group tag \"" + volumeGroup.tag + "\" was already declared, you cannot have two volume groups with the same tag")); } } } public VolumeGroup TryGetVolumeGroupOrDefault(string? tag) { if (tag == null || !loadedScriptVolumeGroups.ContainsKey(tag)) { return DefaultVolumeGroup; } return loadedScriptVolumeGroups[tag]; } public bool TryGetVolumeGroup(string? tag, [NotNullWhen(true)] out VolumeGroup? volumeGroup) { if (tag == null || !loadedScriptVolumeGroups.ContainsKey(tag)) { volumeGroup = null; return false; } volumeGroup = loadedScriptVolumeGroups[tag]; return true; } public void UpdateTimers(float deltaTime) { foreach (Timer value in activeTimers.Values) { value.time += deltaTime; } } public void ClearTimers() { activeTimers.Clear(); } internal void ClearTimer(string timerName) { activeTimers.Remove(timerName); } } public class ScriptManager { public readonly List<Script> loadedScripts = new List<Script>(); public ManualLogSource Logger { get; private set; } public ScriptManager(string[] scriptNames, GameEventListener gameEventListener) { Logger = Logger.CreateLogSource("PizzaTowerEscapeMusic ScriptManager"); Script script = new Script(); loadedScripts.Add(script); foreach (string text in scriptNames) { Script script2 = DeserializeScript(text); if (script2 != null) { script2.Initialise(Logger); if (script2.isAddon) { List<Script.VolumeGroup> list = script.volumeGroups.ToList(); list.AddRange(script2.volumeGroups); script.volumeGroups = list.ToArray(); List<ScriptEvent> list2 = script.scriptEvents.ToList(); list2.AddRange(script2.scriptEvents); script.scriptEvents = list2.ToArray(); } else { loadedScripts.Add(script2); } if (script2.isAddon) { Logger.LogInfo((object)("Script (" + text + ") loaded as addon")); } else { Logger.LogInfo((object)("Script (" + text + ") loaded")); } } } script.Initialise(Logger); gameEventListener.OnFrameUpdate = (Action)Delegate.Combine(gameEventListener.OnFrameUpdate, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.FrameUpdated); }); gameEventListener.OnShipLanded = (Action)Delegate.Combine(gameEventListener.OnShipLanded, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.ShipLanded); }); gameEventListener.OnShipTakeOff = (Action)Delegate.Combine(gameEventListener.OnShipTakeOff, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.ShipTakeOff); }); gameEventListener.OnShipLeavingAlertCalled = (Action)Delegate.Combine(gameEventListener.OnShipLeavingAlertCalled, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.ShipLeavingAlertCalled); }); gameEventListener.OnPlayerDamaged = (Action)Delegate.Combine(gameEventListener.OnPlayerDamaged, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.PlayerDamaged); }); gameEventListener.OnPlayerDeath = (Action)Delegate.Combine(gameEventListener.OnPlayerDeath, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.PlayerDied); }); gameEventListener.OnPlayerEnteredFacility = (Action)Delegate.Combine(gameEventListener.OnPlayerEnteredFacility, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.PlayerEnteredFacility); }); gameEventListener.OnPlayerExitedFacility = (Action)Delegate.Combine(gameEventListener.OnPlayerExitedFacility, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.PlayerExitedFacility); }); gameEventListener.OnPlayerEnteredShip = (Action)Delegate.Combine(gameEventListener.OnPlayerEnteredShip, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.PlayerEnteredShip); }); gameEventListener.OnPlayerExitedShip = (Action)Delegate.Combine(gameEventListener.OnPlayerExitedShip, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.PlayerExitedShip); }); gameEventListener.OnApparatusTaken = (Action)Delegate.Combine(gameEventListener.OnApparatusTaken, (Action)delegate { CheckScriptEvents(ScriptEvent.GameEventType.ApparatusTaken); }); gameEventListener.OnCurrentMoonChanged = (Action<SelectableLevel>)Delegate.Combine(gameEventListener.OnCurrentMoonChanged, (Action<SelectableLevel>)delegate { CheckScriptEvents(ScriptEvent.GameEventType.CurrentMoonChanged); }); Logger.LogInfo((object)"Done loading scripts"); } private void CheckScriptEvents(ScriptEvent.GameEventType eventType) { foreach (Script loadedScript in loadedScripts) { if (!loadedScript.loadedScriptEvents.TryGetValue(eventType, out List<ScriptEvent> value)) { continue; } foreach (ScriptEvent item in value) { if (item.CheckConditions(loadedScript)) { Logger.LogDebug((object)("Conditions for a script event have been met!\n Script Event Type: " + item.scriptEventType + $"\n Game Event Type: {item.gameEventType}" + "\n Comment: " + item.comment)); item.Run(loadedScript); } } } } private Script? DeserializeScript(string name) { string filePath = CustomManager.GetFilePath("Scripts/" + name + ".json", "DefaultScripts/" + name + ".json"); if (!File.Exists(filePath)) { Logger.LogError((object)("Script \"" + name + "\" does not exist! Make sure you spelt it right the config, and make sure its file extension is \".json\"")); return null; } string text = File.ReadAllText(filePath); try { return JsonConvert.DeserializeObject<Script>(text); } catch (Exception ex) { Logger.LogError((object)("Failed to deserialize script \"" + name + "\":\n" + ex.Message)); return null; } } public void UpdateAllScriptTimers(float deltaTime) { foreach (Script loadedScript in loadedScripts) { loadedScript.UpdateTimers(deltaTime); } } public void ClearAllScriptTimers() { foreach (Script loadedScript in loadedScripts) { loadedScript.ClearTimers(); } } } } namespace PizzaTowerEscapeMusic.Scripting.ScriptEvents { public class ScriptEventConverter : JsonConverter<ScriptEvent> { public override bool CanWrite => false; public override ScriptEvent ReadJson(JsonReader reader, Type objectType, ScriptEvent? existingValue, bool hasExistingValue, JsonSerializer serializer) { JObject val = JObject.Load(reader); JToken val2 = default(JToken); if (!val.TryGetValue("scriptEventType", ref val2)) { throw new Exception("scriptEventType type is null!"); } ScriptEvent scriptEvent = Extensions.Value<string>((IEnumerable<JToken>)val2) switch { "PlayMusic" => new ScriptEvent_PlayMusic(), "StopMusic" => new ScriptEvent_StopMusic(), "ResetTimers" => new ScriptEvent_ResetTimers(), "SetVolumeGroupMasterVolume" => new ScriptEvent_SetVolumeGroupMasterVolume(), _ => throw new Exception($"Condition type \"{val2}\" does not exist"), }; serializer.Populate(((JToken)val).CreateReader(), (object)scriptEvent); return scriptEvent; } public override void WriteJson(JsonWriter writer, ScriptEvent? value, JsonSerializer serializer) { throw new NotImplementedException(); } } [JsonConverter(typeof(ScriptEventConverter))] public abstract class ScriptEvent { public enum GameEventType { FrameUpdated, ShipLanded, ShipTakeOff, ShipLeavingAlertCalled, PlayerDamaged, PlayerDied, PlayerEnteredFacility, PlayerExitedFacility, PlayerEnteredShip, PlayerExitedShip, ApparatusTaken, CurrentMoonChanged } public string comment = string.Empty; [JsonRequired] public string scriptEventType = string.Empty; [JsonRequired] public GameEventType gameEventType; public Condition[] conditions = Array.Empty<Condition>(); public bool CheckConditions(Script script) { Script script2 = script; return !conditions.Any((Condition c) => !c.Check(script2)); } public abstract void Run(Script script); } public class ScriptEvent_PlayMusic : ScriptEvent { public enum OverlapHandling { IgnoreAll, IgnoreTag, OverrideAll, OverrideTag, OverrideFadeAll, OverrideFadeTag, Overlap } public bool loop; public bool silenceGameMusic = true; [JsonRequired] public OverlapHandling overlapHandling; public string? tag; [JsonRequired] public string[] musicNames = Array.Empty<string>(); public override void Run(Script script) { if (musicNames.Length != 0) { PizzaTowerEscapeMusicManager.MusicManager.PlayMusic(script, this); } } } public class ScriptEvent_StopMusic : ScriptEvent { public string[]? targetTags; public bool instant; public override void Run(Script script) { if (targetTags != null) { string[] array = targetTags; foreach (string targetTag in array) { if (instant) { PizzaTowerEscapeMusicManager.MusicManager.StopMusic(targetTag); } else { PizzaTowerEscapeMusicManager.MusicManager.FadeStopMusic(targetTag); } } } else if (instant) { PizzaTowerEscapeMusicManager.MusicManager.StopMusic(); } else { PizzaTowerEscapeMusicManager.MusicManager.FadeStopMusic(); } } } public class ScriptEvent_ResetTimers : ScriptEvent { public string[]? targetTimerNames; public override void Run(Script script) { if (targetTimerNames == null) { script.ClearTimers(); return; } string[] array = targetTimerNames; foreach (string timerName in array) { script.ClearTimer(timerName); } } } public class ScriptEvent_SetVolumeGroupMasterVolume : ScriptEvent { public string[] targetTags = Array.Empty<string>(); [JsonRequired] public float masterVolume; public override void Run(Script script) { if (targetTags.Length == 0) { Script.DefaultVolumeGroup.masterVolume = masterVolume; return; } string[] array = targetTags; foreach (string text in array) { if (!script.loadedScriptVolumeGroups.TryGetValue(text, out Script.VolumeGroup value)) { PizzaTowerEscapeMusicManager.ScriptManager.Logger.LogError((object)("Script Event SetVolumeGroupMasterVolume was called for volume group with tag \"" + text + "\", but there is no volume group of that tag")); } else { value.masterVolume = masterVolume; } } } } } namespace PizzaTowerEscapeMusic.Scripting.Conditions { public class ConditionConverter : JsonConverter<Condition> { public override bool CanWrite => false; public override Condition ReadJson(JsonReader reader, Type objectType, Condition? existingValue, bool hasExistingValue, JsonSerializer serializer) { JObject val = JObject.Load(reader); JToken val2 = default(JToken); if (!val.TryGetValue("conditionType", ref val2)) { throw new Exception("Condition type is null!"); } Condition condition = Extensions.Value<string>((IEnumerable<JToken>)val2) switch { "And" => new Condition_And(), "Or" => new Condition_Or(), "Not" => new Condition_Not(), "Weather" => new Condition_Weather(), "PlayerLocation" => new Condition_PlayerLocation(), "PlayerAlive" => new Condition_PlayerAlive(), "PlayerHealth" => new Condition_PlayerHealth(), "PlayerCrouching" => new Condition_PlayerCrouching(), "PlayerInsanity" => new Condition_PlayerInsanity(), "ShipLanded" => new Condition_ShipLanded(), "ShipLeavingAlertCalled" => new Condition_ShipLeavingAlertCalled(), "MusicWithTagPlaying" => new Condition_MusicWithTagPlaying(), "CurrentMoon" => new Condition_CurrentMoon(), "Timer" => new Condition_Timer(), "Random" => new Condition_Random(), "ApparatusDocked" => new Condition_ApparatusDocked(), "TimeOfDay" => new Condition_TimeOfDay(), _ => throw new Exception($"Condition type \"{val2}\" does not exist"), }; serializer.Populate(((JToken)val).CreateReader(), (object)condition); return condition; } public override void WriteJson(JsonWriter writer, Condition? value, JsonSerializer serializer) { throw new NotImplementedException(); } } [JsonConverter(typeof(ConditionConverter))] public abstract class Condition { [JsonRequired] public string conditionType = string.Empty; public abstract bool Check(Script script); } public abstract class ConditionComparableNumber : Condition { public enum ComparisonType { Equals, NotEquals, GreaterThan, LessThan, GreaterThanOrEquals, LessThanOrEquals } [JsonRequired] public ComparisonType comparisonType; } public class Condition_And : Condition { [JsonRequired] public Condition[] conditions = Array.Empty<Condition>(); public override bool Check(Script script) { Script script2 = script; return !conditions.Any((Condition c) => !c.Check(script2)); } } public class Condition_Or : Condition { [JsonRequired] public Condition[] conditions = Array.Empty<Condition>(); public override bool Check(Script script) { Script script2 = script; return conditions.Any((Condition c) => c.Check(script2)); } } public class Condition_Not : Condition { [JsonRequired] public Condition? condition; public override bool Check(Script script) { if (condition == null) { return true; } return !condition.Check(script); } } public class Condition_Weather : Condition { [JsonRequired] public LevelWeatherType weather; public override bool Check(Script script) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)TimeOfDay.Instance == (Object)null) { return false; } return TimeOfDay.Instance.currentLevelWeather == weather; } } public class Condition_PlayerLocation : Condition { public enum Location { Ship, Facility } [JsonRequired] public Location location; public override bool Check(Script script) { if ((Object)(object)GameNetworkManager.Instance == (Object)null) { return false; } if ((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) { return false; } return location switch { Location.Ship => GameNetworkManager.Instance.localPlayerController.isInHangarShipRoom, Location.Facility => GameNetworkManager.Instance.localPlayerController.isInsideFactory, _ => false, }; } } public class Condition_PlayerAlive : Condition { public override bool Check(Script script) { if ((Object)(object)GameNetworkManager.Instance == (Object)null) { return false; } if ((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) { return false; } return !GameNetworkManager.Instance.localPlayerController.isPlayerDead; } } public class Condition_PlayerHealth : ConditionComparableNumber { [JsonRequired] public int value; public override bool Check(Script script) { if ((Object)(object)GameNetworkManager.Instance == (Object)null) { return false; } if ((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) { return false; } int health = GameNetworkManager.Instance.localPlayerController.health; return comparisonType switch { ComparisonType.Equals => health == value, ComparisonType.NotEquals => health != value, ComparisonType.GreaterThan => health > value, ComparisonType.LessThan => health < value, ComparisonType.GreaterThanOrEquals => health >= value, ComparisonType.LessThanOrEquals => health <= value, _ => false, }; } } public class Condition_PlayerCrouching : Condition { public override bool Check(Script script) { if ((Object)(object)GameNetworkManager.Instance == (Object)null) { return false; } if ((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) { return false; } return GameNetworkManager.Instance.localPlayerController.isCrouching; } } public class Condition_PlayerInsanity : ConditionComparableNumber { [JsonRequired] public float level; public override bool Check(Script script) { if ((Object)(object)GameNetworkManager.Instance == (Object)null) { return false; } if ((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) { return false; } float num = GameNetworkManager.Instance.localPlayerController.insanityLevel / GameNetworkManager.Instance.localPlayerController.maxInsanityLevel; return comparisonType switch { ComparisonType.Equals => level == num, ComparisonType.NotEquals => level != num, ComparisonType.GreaterThan => level > num, ComparisonType.LessThan => level < num, ComparisonType.GreaterThanOrEquals => level >= num, ComparisonType.LessThanOrEquals => level <= num, _ => false, }; } } public class Condition_ShipLanded : Condition { public override bool Check(Script script) { if ((Object)(object)StartOfRound.Instance == (Object)null) { return false; } return StartOfRound.Instance.shipHasLanded; } } public class Condition_ShipLeavingAlertCalled : Condition { public override bool Check(Script script) { if ((Object)(object)TimeOfDay.Instance == (Object)null) { return false; } return TimeOfDay.Instance.shipLeavingAlertCalled; } } public class Condition_MusicWithTagPlaying : Condition { [JsonRequired] public string tag = string.Empty; public override bool Check(Script script) { return PizzaTowerEscapeMusicManager.MusicManager.GetIsMusicPlaying(tag); } } public class Condition_CurrentMoon : Condition { private static readonly Dictionary<string, int> moonNameToId = new Dictionary<string, int>(); [JsonRequired] public string moonName = string.Empty; private bool isDisabled; public override bool Check(Script script) { if ((Object)(object)TimeOfDay.Instance == (Object)null) { return false; } if ((Object)(object)StartOfRound.Instance == (Object)null) { return false; } if (isDisabled) { return false; } if (!moonNameToId.TryGetValue(moonName, out var value)) { SelectableLevel[] levels = StartOfRound.Instance.levels; foreach (SelectableLevel val in levels) { moonNameToId.TryAdd(val.PlanetName, val.levelID); } if (!moonNameToId.TryGetValue(moonName, out value)) { PizzaTowerEscapeMusicManager.ScriptManager.Logger.LogError((object)("From CurrentMoon condition: Found no existing level with the name \"" + moonName + "\"")); isDisabled = true; return false; } } return TimeOfDay.Instance.currentLevel.levelID == value; } } public class Condition_Timer : Condition { [JsonRequired] public string timerName = string.Empty; [JsonRequired] public float timeGoal; public bool resetsTimer = true; public override bool Check(Script script) { if (!script.activeTimers.TryGetValue(timerName, out Script.Timer value)) { value = new Script.Timer(timerName); script.activeTimers.Add(timerName, value); } if (value.time >= timeGoal) { if (resetsTimer) { value.time = 0f; } return true; } return false; } } public class Condition_Random : Condition { [JsonRequired] public float chance; public override bool Check(Script script) { return Random.Range(0f, 1f) <= chance; } } public class Condition_ApparatusDocked : Condition { public override bool Check(Script script) { return GameEventListener.IsApparatusDocked(); } } public class Condition_TimeOfDay : ConditionComparableNumber { [JsonRequired] public float time; public override bool Check(Script script) { if ((Object)(object)TimeOfDay.Instance == (Object)null) { return false; } float num = TimeOfDay.Instance.currentDayTime / TimeOfDay.Instance.totalTime; return comparisonType switch { ComparisonType.Equals => num == time, ComparisonType.NotEquals => num != time, ComparisonType.GreaterThan => num > time, ComparisonType.LessThan => num < time, ComparisonType.GreaterThanOrEquals => num >= time, ComparisonType.LessThanOrEquals => num <= time, _ => false, }; } } }