Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Subtitles v2.1.1
Subtitles.dll
Decompiled 2 years agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; 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.Text; using System.Timers; using AudibleDistanceLib; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using SubtitlesAPI; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Subtitles")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("Displays text for in-game sounds to help make the game more accessible for Deaf/HoH gamers.")] [assembly: AssemblyFileVersion("2.1.1.0")] [assembly: AssemblyInformationalVersion("2.1.1+4f99db70e06b5ed7de3e4263d547e4e95c0494f8")] [assembly: AssemblyProduct("Subtitles")] [assembly: AssemblyTitle("Subtitles")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.1.1.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.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Subtitles { public class Constants { public static readonly int DefaultExpireSubtitleTimeMs = 5000; public static readonly int DefaultVisibleSubtitleLines = 3; public static readonly string HtmlLineBreakTag = "<br>"; public static readonly string PlayerScreenGUIName = "Systems/UI/Canvas/Panel/GameObject/PlayerScreen"; } [BepInPlugin("JustJelly.Subtitles", "Subtitles", "2.1.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { private const string pluginGuid = "JustJelly.Subtitles"; private const string pluginName = "Subtitles"; private const string pluginVersion = "2.1.1"; private Harmony harmony; public static Plugin Instance; public static ManualLogSource ManualLogSource; public SubtitleList subtitles = new SubtitleList(); public ConfigEntry<float> minimumAudibleVolume; public ConfigEntry<bool> logSoundNames; private void Awake() { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown if (Instance == null) { Instance = this; } ManualLogSource = Logger.CreateLogSource("JustJelly.Subtitles"); ManualLogSource.LogInfo((object)"Subtitles 2.1.1 loaded!"); minimumAudibleVolume = ((BaseUnityPlugin)this).Config.Bind<float>("\u200bOptions", "MinimumAudibleVolume", 12f, "The minimum volume this mod determines is audible. Scale of 0-100. Any sound heard above this volume will be displayed on subtitles, any sound below will not."); logSoundNames = ((BaseUnityPlugin)this).Config.Bind<bool>("Contributors/Developers", "LogSoundNames", false, "Whether the mod should log the names of sounds. Only valuable if trying to add more subtitles / localization."); harmony = new Harmony("JustJelly.Subtitles"); harmony.PatchAll(); } } public class SubtitleList : IList<string>, ICollection<string>, IEnumerable<string>, IEnumerable { private volatile List<Tuple<DateTime, string>> collection = new List<Tuple<DateTime, string>>(); private readonly Timer timer; private readonly TimeSpan expiration; public string this[int index] { get { return collection[index].Item2; } set { collection[index] = new Tuple<DateTime, string>(DateTime.Now, value); } } public int Count => collection.Count; public bool IsSynchronized => false; public bool IsReadOnly => false; public SubtitleList() { timer = new Timer { Interval = 1000.0 }; timer.Elapsed += RemoveExpiredElements; timer.Start(); expiration = TimeSpan.FromMilliseconds(Constants.DefaultExpireSubtitleTimeMs); } private void RemoveExpiredElements(object sender, EventArgs e) { for (int num = collection.Count - 1; num >= 0; num--) { if (DateTime.Now - collection[num].Item1 >= expiration) { collection.RemoveAt(num); } } } public List<string> TakeLast(int number) { return (from element in collection where DateTime.Now >= element.Item1 orderby element.Item1 select element.Item2).TakeLast(number).ToList(); } public IEnumerator<string> GetEnumerator() { return collection.Select((Tuple<DateTime, string> x) => x.Item2).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return collection.Select((Tuple<DateTime, string> x) => x.Item2).GetEnumerator(); } public void Add(string item) { collection.Add(new Tuple<DateTime, string>(DateTime.Now, item)); } public void Add(string item, float seconds) { collection.Add(new Tuple<DateTime, string>(DateTime.Now.AddSeconds(seconds), item)); } public void CopyTo(string[] array, int index) { for (int i = 0; i < collection.Count; i++) { array[i + index] = collection[i].Item2; } } public bool Remove(string item) { bool result = Contains(item); for (int num = collection.Count - 1; num >= 0; num--) { if (collection[num].Item2 == item) { collection.RemoveAt(num); } } return result; } public void RemoveAt(int i) { collection.RemoveAt(i); } public bool Contains(string item) { for (int i = 0; i < collection.Count; i++) { if (collection[i].Item2 == item) { return true; } } return false; } public void Insert(int index, string item) { collection.Insert(index, new Tuple<DateTime, string>(DateTime.Now, item)); } public int IndexOf(string item) { for (int i = 0; i < collection.Count; i++) { if (collection[i].Item2 == item) { return i; } } return -1; } public void Clear() { collection.Clear(); } } public static class PluginInfo { public const string PLUGIN_GUID = "Subtitles"; public const string PLUGIN_NAME = "Subtitles"; public const string PLUGIN_VERSION = "2.1.1"; } } namespace Subtitles.Patches { [HarmonyPatch(typeof(AudioSource))] public class AudioSourcePatch { [HarmonyPrefix] [HarmonyPatch("PlayOneShotHelper", new Type[] { typeof(AudioSource), typeof(AudioClip), typeof(float) })] public static void PlayOneShotHelper_Prefix(AudioSource source, ref AudioClip clip, float volumeScale) { if (AudibleDistanceLib.IsInWithinAudiableDistance(GameNetworkManager.Instance, source, volumeScale, Plugin.Instance.minimumAudibleVolume.Value)) { AddSubtitle(clip); } } [HarmonyPrefix] [HarmonyPatch("Play", new Type[] { typeof(double) })] public static void PlayDelayed_Prefix(AudioSource __instance) { if (!((Object)(object)__instance.clip == (Object)null) && AudibleDistanceLib.IsInWithinAudiableDistance(GameNetworkManager.Instance, __instance, __instance.volume, Plugin.Instance.minimumAudibleVolume.Value)) { AddSubtitle(__instance.clip); } } [HarmonyPrefix] [HarmonyPatch("Play", new Type[] { })] public static void Play_Prefix(AudioSource __instance) { if (!((Object)(object)__instance.clip == (Object)null) && AudibleDistanceLib.IsInWithinAudiableDistance(GameNetworkManager.Instance, __instance, __instance.volume, Plugin.Instance.minimumAudibleVolume.Value)) { AddSubtitle(__instance.clip); } } private static void AddSubtitle(AudioClip clip) { if (((clip != null) ? ((Object)clip).name : null) == null) { return; } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(((Object)clip).name); if (SubtitlesAPI.Localization.Translations.TryGetValue(fileNameWithoutExtension, out var value)) { if (Plugin.Instance.logSoundNames.Value) { Plugin.ManualLogSource.LogInfo((object)("Found translation for " + fileNameWithoutExtension + "!")); } Plugin.Instance.subtitles.Add(FormatSoundTranslation(value)); return; } if (SubtitlesAPI.Localization.DialogueTranslations.TryGetValue(fileNameWithoutExtension, out List<(float, string)> value2)) { if (Plugin.Instance.logSoundNames.Value) { Plugin.ManualLogSource.LogInfo((object)("Found dialogue translation for " + fileNameWithoutExtension + "!")); } { foreach (var (seconds, translation) in value2) { Plugin.Instance.subtitles.Add(ForamtDialogueTranslation(translation), seconds); } return; } } if (Plugin.Instance.logSoundNames.Value) { Plugin.ManualLogSource.LogInfo((object)("No translation for " + fileNameWithoutExtension + ".")); } } private static string FormatSoundTranslation(string translation) { if (translation.StartsWith("[") && translation.EndsWith("]")) { return "<color=yellow>" + translation + "</color>"; } return "<color=yellow>[" + translation + "]</color>"; } private static string ForamtDialogueTranslation(string translation) { if (translation.StartsWith("[") && translation.EndsWith("]")) { return FormatSoundTranslation(translation); } return "<color=green>" + translation + "</color>"; } } [HarmonyPatch(typeof(HUDManager))] public class HUDManagerPatch { private static TextMeshProUGUI subtitleGUItext; [HarmonyPostfix] [HarmonyPatch("Awake")] private static void Awake_Postfix(ref HUDManager __instance) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("SubtitlesGUI"); val.AddComponent<RectTransform>(); TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>(); RectTransform rectTransform = ((TMP_Text)val2).rectTransform; ((Transform)rectTransform).SetParent(GameObject.Find(Constants.PlayerScreenGUIName).transform, false); rectTransform.sizeDelta = new Vector2(600f, 200f); rectTransform.anchoredPosition = new Vector2(0f, -125f); ((TMP_Text)val2).alignment = (TextAlignmentOptions)514; ((TMP_Text)val2).font = ((TMP_Text)__instance.controlTipLines[0]).font; ((TMP_Text)val2).fontSize = 14f; subtitleGUItext = val2; } [HarmonyPostfix] [HarmonyPatch("Update")] private static void Update_Postfix() { ((TMP_Text)subtitleGUItext).text = GetLatestSubtitles(); } private static string GetLatestSubtitles() { StringBuilder stringBuilder = new StringBuilder(); IList<string> list = Plugin.Instance.subtitles.TakeLast(Constants.DefaultVisibleSubtitleLines).ToList(); string value = string.Empty; foreach (string item in list) { stringBuilder.Append(value); stringBuilder.Append(item); value = Constants.HtmlLineBreakTag; } return stringBuilder.ToString(); } } }