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 Piano v1.0.1
PianoHints.dll
Decompiled 8 months agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.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; } } } [BepInPlugin("com.igor.repo.pianohints", "Piano Hints", "1.0.0")] public class PianoHintsPlugin : BaseUnityPlugin { public const string PluginGuid = "com.igor.repo.pianohints"; public const string PluginName = "Piano Hints"; public const string PluginVersion = "1.0.0"; internal static ManualLogSource LogS; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<KeyboardShortcut> ToggleKey; internal static ConfigEntry<float> HeightOffset; internal static ConfigEntry<float> SquareSize; internal static ConfigEntry<float> KeyThickness; internal static ConfigEntry<float> KeyLength; internal static ConfigEntry<int> FontSize; internal static ConfigEntry<int> ColorCycle; internal static ConfigEntry<bool> Billboard; internal static ConfigEntry<float> MaxDrawDistance; internal static ConfigEntry<PianoLabelMode> LabelMode; internal static ConfigEntry<string> NoteNamesCSV; internal static ConfigEntry<int> NoteRootIndex; internal static ConfigEntry<int> BaseOctave; internal static ConfigEntry<float> LabelRotX; internal static ConfigEntry<float> LabelRotY; internal static ConfigEntry<float> LabelRotZ; internal static ConfigEntry<float> LabelLift; internal static ConfigEntry<bool> CompactSemitoneView; internal static ConfigEntry<float> KeySaturation; internal static ConfigEntry<float> KeyValue; internal static ConfigEntry<float> KeyAlpha; internal static ConfigEntry<KeyboardShortcut> TeacherStartKey; internal static ConfigEntry<int> TeacherStartKeyIndex; internal static ConfigEntry<string> TeacherSong; internal static ConfigEntry<string> TeacherHighlightColor; internal static ConfigEntry<string> TeacherScripts; private static Dictionary<string, List<int>> _songScripts = new Dictionary<string, List<int>>(StringComparer.OrdinalIgnoreCase); private void Awake() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Unknown result type (might be due to invalid IL or missing references) //IL_03e0: Unknown result type (might be due to invalid IL or missing references) LogS = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn the piano hint overlay on/off."); ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "ToggleKey", new KeyboardShortcut((KeyCode)291, Array.Empty<KeyCode>()), "Toggle piano hints visibility."); HeightOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "HeightOffset", 0.02f, "Vertical offset above the key line (meters)."); SquareSize = ((BaseUnityPlugin)this).Config.Bind<float>("_Deprecated", "SquareSize", 0.08f, "Deprecated; use KeyThickness/KeyLength."); KeyThickness = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "KeyThickness", 0.025f, "Thickness of each key hint along the key row (meters)."); KeyLength = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "KeyLength", 0.4f, "Length of each key hint into the piano (meters)."); FontSize = ((BaseUnityPlugin)this).Config.Bind<int>("Visual", "FontSize", 64, "Font size for labels (TextMesh.fontSize)."); ColorCycle = ((BaseUnityPlugin)this).Config.Bind<int>("Visual", "ColorCycle", 12, "Number of distinct colors to cycle through for keys."); Billboard = ((BaseUnityPlugin)this).Config.Bind<bool>("Visual", "Billboard", true, "If true, hints face the main camera."); MaxDrawDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "MaxDrawDistance", 60f, "Do not show hints when the piano is farther than this (meters). 0 = always show."); LabelMode = ((BaseUnityPlugin)this).Config.Bind<PianoLabelMode>("Labels", "LabelMode", PianoLabelMode.None, "Label mode: None, Index, Note, NoteOctave, ClipName."); NoteNamesCSV = ((BaseUnityPlugin)this).Config.Bind<string>("Labels", "NoteNamesCSV", "C,C#,D,D#,E,F,F#,G,G#,A,A#,B", "Comma-separated base note names for one octave."); NoteRootIndex = ((BaseUnityPlugin)this).Config.Bind<int>("Labels", "NoteRootIndex", 0, "Index in NoteNames for the first base sample (0=C,1=C#,...)."); BaseOctave = ((BaseUnityPlugin)this).Config.Bind<int>("Labels", "BaseOctave", 4, "Octave number for the first base sample (used by NoteOctave mode)."); LabelRotX = ((BaseUnityPlugin)this).Config.Bind<float>("Labels", "LabelRotX", 0f, "Extra local X rotation applied to label text (degrees)."); LabelRotY = ((BaseUnityPlugin)this).Config.Bind<float>("Labels", "LabelRotY", 0f, "Extra local Y rotation applied to label text (degrees)."); LabelRotZ = ((BaseUnityPlugin)this).Config.Bind<float>("Labels", "LabelRotZ", 0f, "Extra local Z rotation applied to label text (degrees)."); LabelLift = ((BaseUnityPlugin)this).Config.Bind<float>("Labels", "LabelLift", 0.003f, "Separation from key surface along key normal (meters). Helps avoid z-fighting."); CompactSemitoneView = ((BaseUnityPlugin)this).Config.Bind<bool>("_Deprecated", "CompactSemitoneView", false, "Deprecated; previously drew one bar per semitone group."); KeySaturation = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "KeySaturation", 0.5f, "Saturation (0..1) for key colors."); KeyValue = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "KeyValue", 0.8f, "Value/Brightness (0..1) for key colors."); KeyAlpha = ((BaseUnityPlugin)this).Config.Bind<float>("Visual", "KeyAlpha", 0.45f, "Alpha for key colors."); TeacherStartKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Teacher", "StartHotkey", new KeyboardShortcut((KeyCode)111, Array.Empty<KeyCode>()), "Press to start/stop the song helper."); TeacherStartKeyIndex = ((BaseUnityPlugin)this).Config.Bind<int>("Teacher", "StartKeyIndex", 0, "Absolute key index to use as the song's first note (S). 0 = beginning of keyboard."); TeacherSong = ((BaseUnityPlugin)this).Config.Bind<string>("Teacher", "Song", "happybirthday", "Default song id to use."); TeacherHighlightColor = ((BaseUnityPlugin)this).Config.Bind<string>("Teacher", "HighlightColor", "#FFD200FF", "RGBA hex for highlight color, e.g. #FFD200FF."); TeacherScripts = ((BaseUnityPlugin)this).Config.Bind<string>("Teacher", "Scripts", "birthday: 55,55,57,55,60,59,55,55,57,55,62,60; sunshine: 60,60,62,64,64,64,62,64,60,60,60,62,64,65,69,69,67,65,64,60,62,64,65,69,69,67,65,64,60,60,62,64,64,62,62,64,60; smoke: 19,22,24,19,22,25,24,19,22,24,22,19; elise: 64,63,64,63,64,59,62,60,57,48,52,57,59,52,56,59,60; korobeiniki: 64,58,60,62,60,58,56,56,60,64,62,60,58,60,62,64,60,56,56", "Song scripts. Format: name: comma-separated integer offsets per note; multiple lines or ';' separated pairs allowed."); try { ParseScripts(TeacherScripts.Value); } catch { } try { new Harmony("com.igor.repo.pianohints").PatchAll(typeof(PianoHintsPlugin).Assembly); } catch (Exception ex) { ManualLogSource logS = LogS; if (logS != null) { logS.LogWarning((object)("Harmony patch failed: " + ex.Message)); } } BuildOrAttachHost(); } private void Update() { //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) KeyboardShortcut value = ToggleKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { Enabled.Value = !Enabled.Value; ManualLogSource logS = LogS; if (logS != null) { logS.LogInfo((object)string.Format("[{0}] Enabled = {1}", "Piano Hints", Enabled.Value)); } PianoHintsOverlay[] array = Object.FindObjectsOfType<PianoHintsOverlay>(true); for (int i = 0; i < array.Length; i++) { array[i].ApplyVisibility(Enabled.Value); } } } private void BuildOrAttachHost() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown GameObject val = GameObject.Find("PianoHintsHost (BepInEx)"); if ((Object)(object)val == (Object)null) { val = new GameObject("PianoHintsHost (BepInEx)"); Object.DontDestroyOnLoad((Object)(object)val); ((Object)val).hideFlags = (HideFlags)61; ManualLogSource logS = LogS; if (logS != null) { logS.LogInfo((object)"Created persistent PianoHints host GameObject."); } } if ((Object)(object)val.GetComponent<PianoHintsSweeper>() == (Object)null) { val.AddComponent<PianoHintsSweeper>(); ManualLogSource logS2 = LogS; if (logS2 != null) { logS2.LogInfo((object)"Attached PianoHintsSweeper to host."); } } } private static void ParseScripts(string scripts) { _songScripts.Clear(); if (string.IsNullOrWhiteSpace(scripts)) { return; } string[] array = scripts.Replace("\r", "").Split(new char[2] { '\n', ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0) { continue; } int num = text.IndexOf(':'); if (num <= 0) { continue; } string text2 = text.Substring(0, num).Trim(); string[] array2 = text.Substring(num + 1).Split(new char[3] { ',', ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); List<int> list = new List<int>(array2.Length); string[] array3 = array2; for (int j = 0; j < array3.Length; j++) { if (int.TryParse(array3[j].Trim(), out var result)) { list.Add(result); } } if (text2.Length > 0 && list.Count > 0) { _songScripts[text2] = list; } } if (_songScripts.Count == 0) { _songScripts["happybirthday"] = new List<int> { 0, 0, 2, 0, 5, 4, 0, 0, 2, 0, 7, 5 }; } } internal static bool TryStartSongFromChat(string id) { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) try { if (string.IsNullOrWhiteSpace(id)) { id = TeacherSong.Value; } if (string.IsNullOrWhiteSpace(id)) { return false; } PianoSongTeacher[] array = Object.FindObjectsOfType<PianoSongTeacher>(true); if (array == null || array.Length == 0) { return false; } Transform val = (Object.op_Implicit((Object)(object)PlayerAvatar.instance) ? ((Component)PlayerAvatar.instance).transform : null); PianoSongTeacher pianoSongTeacher = array[0]; float num = float.MaxValue; PianoSongTeacher[] array2 = array; foreach (PianoSongTeacher pianoSongTeacher2 in array2) { if (!((Object)(object)pianoSongTeacher2 == (Object)null)) { float num2 = (Object.op_Implicit((Object)(object)val) ? Vector3.SqrMagnitude(((Component)pianoSongTeacher2).transform.position - val.position) : 0f); if (num2 < num) { num = num2; pianoSongTeacher = pianoSongTeacher2; } } } if ((Object)(object)pianoSongTeacher == (Object)null) { return false; } pianoSongTeacher.StartSongExternal(id, TeacherStartKeyIndex.Value); ManualLogSource logS = LogS; if (logS != null) { logS.LogInfo((object)("[Teacher] Starting song '" + id + "' from chat.")); } return true; } catch (Exception ex) { ManualLogSource logS2 = LogS; if (logS2 != null) { logS2.LogWarning((object)("[Teacher] Start from chat failed: " + ex.Message)); } return false; } } internal static bool TryGetSongOffsets(string id, out List<int> offsets) { if (_songScripts.TryGetValue(id ?? string.Empty, out offsets)) { return true; } offsets = null; return false; } } internal static class PianoHintResources { private static Material _hintMat; public static Material HintMat { get { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown if ((Object)(object)_hintMat == (Object)null) { Shader val = Shader.Find("Sprites/Default"); if ((Object)(object)val == (Object)null) { val = Shader.Find("Unlit/Color"); } _hintMat = new Material(val) { name = "PianoHintMat" }; float num = 0.45f; try { num = Mathf.Clamp01(PianoHintsPlugin.KeyAlpha.Value); } catch { } _hintMat.color = new Color(1f, 1f, 1f, num); } return _hintMat; } } public static Color ColorForIndex(int i, int cycle) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) if (cycle <= 0) { cycle = 12; } float num = (float)(i % cycle) / (float)cycle; float num2 = 0.5f; float num3 = 0.8f; float a = 0.45f; try { num2 = Mathf.Clamp01(PianoHintsPlugin.KeySaturation.Value); } catch { } try { num3 = Mathf.Clamp01(PianoHintsPlugin.KeyValue.Value); } catch { } try { a = Mathf.Clamp01(PianoHintsPlugin.KeyAlpha.Value); } catch { } Color result = Color.HSVToRGB(num, num2, num3); result.a = a; return result; } } public class PianoHintsSweeper : MonoBehaviour { private readonly HashSet<int> _processed = new HashSet<int>(); private float _nextScan; private void Update() { if (Time.unscaledTime < _nextScan) { return; } _nextScan = Time.unscaledTime + 1f; try { MusicalValuableLogic[] array = Object.FindObjectsOfType<MusicalValuableLogic>(true); foreach (MusicalValuableLogic val in array) { if ((Object)(object)val == (Object)null) { continue; } int instanceID = ((Object)val).GetInstanceID(); if (_processed.Contains(instanceID)) { continue; } if ((Object)(object)((Component)val).gameObject.GetComponent<PianoHintsOverlay>() == (Object)null) { ((Component)val).gameObject.AddComponent<PianoHintsOverlay>().Initialize(val); ManualLogSource logS = PianoHintsPlugin.LogS; if (logS != null) { logS.LogInfo((object)("[PianoHints] Attached overlay to " + ((Object)((Component)val).gameObject).name)); } } if ((Object)(object)((Component)val).gameObject.GetComponent<PianoSongTeacher>() == (Object)null) { ((Component)val).gameObject.AddComponent<PianoSongTeacher>(); } _processed.Add(instanceID); } } catch (Exception ex) { ManualLogSource logS2 = PianoHintsPlugin.LogS; if (logS2 != null) { logS2.LogWarning((object)("[PianoHintsSweeper] " + ex.GetType().Name + ": " + ex.Message)); } } } } public enum PianoLabelMode { None, Index, Note, NoteOctave, ClipName } public class PianoHintsOverlay : MonoBehaviour { private MusicalValuableLogic _logic; private Transform _start; private Transform _end; private Transform _root; private readonly List<Renderer> _renderers = new List<Renderer>(); private readonly List<Component> _textComps = new List<Component>(); private int _keyCount; private float _lastLayoutCheck; private List<string> _noteNames = new List<string>(); private readonly List<Color> _defaultColors = new List<Color>(); private readonly Dictionary<int, bool> _isHighlighted = new Dictionary<int, bool>(); public int KeyCount => _keyCount; private int GroupSize() { return Mathf.Max(1, ((Object)(object)_logic != (Object)null) ? _logic.numberOfOctaves : 6); } public void Initialize(MusicalValuableLogic logic) { _logic = logic; _start = logic.musicKeysStart; _end = logic.musicKeysEnd; _keyCount = SafeComputeKeyCount(logic); _noteNames = ParseNoteNames(PianoHintsPlugin.NoteNamesCSV.Value); EnsureRoot(); BuildOrRebuild(); ApplyVisibility(PianoHintsPlugin.Enabled.Value); } private int SafeComputeKeyCount(MusicalValuableLogic logic) { try { int num = Mathf.Max(1, (logic.musicKeys != null) ? logic.musicKeys.Count : 0); int num2 = Mathf.Max(1, logic.numberOfOctaves); if (num == 0) { num = 12; } return num * num2; } catch { return 72; } } private void EnsureRoot() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_root != (Object)null)) { GameObject val = new GameObject("PianoHintsRoot"); val.transform.SetParent(((Component)this).transform, false); _root = val.transform; _root.localPosition = Vector3.zero; _root.localRotation = Quaternion.identity; _root.localScale = Vector3.one; } } private void Clear() { for (int num = _root.childCount - 1; num >= 0; num--) { Transform child = _root.GetChild(num); try { Object.Destroy((Object)(object)((Component)child).gameObject); } catch { } } _renderers.Clear(); _textComps.Clear(); } private void BuildOrRebuild() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Expected O, but got Unknown //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Expected O, but got Unknown //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_start == (Object)null || (Object)(object)_end == (Object)null) { ManualLogSource logS = PianoHintsPlugin.LogS; if (logS != null) { logS.LogWarning((object)"[PianoHintsOverlay] musicKeysStart or musicKeysEnd is null; cannot build hints."); } return; } Clear(); Vector3 position = _start.position; Vector3 val = _end.position - position; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude < 0.0001f) { return; } Vector3 val2 = val / magnitude; Material hintMat = PianoHintResources.HintMat; int cycle = Mathf.Max(2, PianoHintsPlugin.ColorCycle.Value); float num = Mathf.Max(0.003f, PianoHintsPlugin.KeyThickness.Value); float num2 = Mathf.Max(0.01f, PianoHintsPlugin.KeyLength.Value); float value = PianoHintsPlugin.HeightOffset.Value; Vector3 up = ((Component)this).transform.up; Vector3 val3 = Vector3.Cross(up, val2); Vector3 normalized = ((Vector3)(ref val3)).normalized; for (int i = 0; i < _keyCount; i++) { float num3 = ((float)i + 0.5f) / (float)_keyCount; Vector3 position2 = position + val2 * (num3 * magnitude) + ((Component)this).transform.up * value; GameObject val4 = GameObject.CreatePrimitive((PrimitiveType)5); ((Object)val4).name = $"Hint_{i:000}"; val4.transform.SetParent(_root, true); val4.transform.position = position2; val4.transform.rotation = Quaternion.LookRotation(up, normalized); val4.transform.localScale = new Vector3(num, num2, 1f); Collider component = val4.GetComponent<Collider>(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } Renderer component2 = val4.GetComponent<Renderer>(); component2.sharedMaterial = hintMat; MaterialPropertyBlock val5 = new MaterialPropertyBlock(); Color val6 = PianoHintResources.ColorForIndex(i, cycle); val5.SetColor("_Color", val6); component2.SetPropertyBlock(val5); _renderers.Add(component2); if (PianoHintsPlugin.LabelMode.Value != 0) { GameObject val7 = new GameObject("Label"); val7.transform.SetParent(val4.transform, false); val7.transform.localPosition = new Vector3(0f, 0f, PianoHintsPlugin.LabelLift.Value); val7.transform.localRotation = Quaternion.Euler(PianoHintsPlugin.LabelRotX.Value, PianoHintsPlugin.LabelRotY.Value, PianoHintsPlugin.LabelRotZ.Value); if (PianoHintsPlugin.Billboard.Value) { val7.AddComponent<PianoHintBillboard>(); } string labelForKey = GetLabelForKey(i); TryAddTextMesh(val7, labelForKey, num); } } } public void ApplyVisibility(bool visible) { if ((Object)(object)_root != (Object)null) { ((Component)_root).gameObject.SetActive(visible); } } private void LateUpdate() { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_logic == (Object)null || (Object)(object)_start == (Object)null || (Object)(object)_end == (Object)null) { return; } if (!PianoHintsPlugin.Enabled.Value) { if ((Object)(object)_root != (Object)null && ((Component)_root).gameObject.activeSelf) { ((Component)_root).gameObject.SetActive(false); } return; } if ((Object)(object)_root != (Object)null && !((Component)_root).gameObject.activeSelf) { ((Component)_root).gameObject.SetActive(true); } float value = PianoHintsPlugin.MaxDrawDistance.Value; if (value > 0f) { Camera main = Camera.main; if ((Object)(object)main != (Object)null) { float num = Vector3.Distance(((Component)main).transform.position, ((Component)this).transform.position); ((Component)_root).gameObject.SetActive(num <= value); } } if (!(Time.unscaledTime - _lastLayoutCheck > 0.5f)) { return; } _lastLayoutCheck = Time.unscaledTime; int num2 = SafeComputeKeyCount(_logic); if (num2 != _keyCount) { _keyCount = num2; _noteNames = ParseNoteNames(PianoHintsPlugin.NoteNamesCSV.Value); BuildOrRebuild(); return; } Vector3 position = _start.position; Vector3 val = _end.position - position; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude < 0.0001f) { return; } Vector3 val2 = val / magnitude; float num3 = Mathf.Max(0.003f, PianoHintsPlugin.KeyThickness.Value); float num4 = Mathf.Max(0.01f, PianoHintsPlugin.KeyLength.Value); float value2 = PianoHintsPlugin.HeightOffset.Value; Vector3 up = ((Component)this).transform.up; Vector3 val3 = Vector3.Cross(up, val2); Vector3 normalized = ((Vector3)(ref val3)).normalized; int num5 = Mathf.Min(_root.childCount, _keyCount); for (int i = 0; i < num5; i++) { float num6 = ((float)i + 0.5f) / (float)_keyCount; Vector3 position2 = position + val2 * (num6 * magnitude) + ((Component)this).transform.up * value2; Transform child = _root.GetChild(i); child.position = position2; child.rotation = Quaternion.LookRotation(up, normalized); child.localScale = new Vector3(num3, num4, 1f); if (child.childCount > 0) { child.GetChild(0).localPosition = new Vector3(0f, 0f, PianoHintsPlugin.LabelLift.Value); } } } private void CacheDefaultColorsIfNeeded() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (_defaultColors.Count != _renderers.Count) { _defaultColors.Clear(); for (int i = 0; i < _renderers.Count; i++) { int cycle = Mathf.Max(2, PianoHintsPlugin.ColorCycle.Value); _defaultColors.Add(PianoHintResources.ColorForIndex(i, cycle)); } } } public void HighlightKey(int keyIndex, Color color) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (keyIndex >= 0 && keyIndex < _renderers.Count) { CacheDefaultColorsIfNeeded(); Renderer obj = _renderers[keyIndex]; MaterialPropertyBlock val = new MaterialPropertyBlock(); obj.GetPropertyBlock(val); val.SetColor("_Color", color); obj.SetPropertyBlock(val); _isHighlighted[keyIndex] = true; } } public void ClearHighlight(int keyIndex) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (keyIndex >= 0 && keyIndex < _renderers.Count) { CacheDefaultColorsIfNeeded(); Renderer obj = _renderers[keyIndex]; MaterialPropertyBlock val = new MaterialPropertyBlock(); obj.GetPropertyBlock(val); val.SetColor("_Color", _defaultColors[keyIndex]); obj.SetPropertyBlock(val); _isHighlighted.Remove(keyIndex); } } public void HighlightGroup(int groupIndex, Color color) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (groupIndex >= 0 && groupIndex < _renderers.Count) { HighlightKey(groupIndex, color); } } public void ClearHighlightGroup(int groupIndex) { if (groupIndex >= 0 && groupIndex < _renderers.Count) { ClearHighlight(groupIndex); } } private List<string> ParseNoteNames(string csv) { List<string> list = new List<string>(); try { if (!string.IsNullOrWhiteSpace(csv)) { string[] array = csv.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { list.Add(text.Trim()); } } } catch { } if (list.Count == 0) { list.AddRange(new string[12] { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" }); } return list; } private string GetLabelForKey(int key) { PianoLabelMode value = PianoHintsPlugin.LabelMode.Value; if (value == PianoLabelMode.Index) { return (key + 1).ToString(); } int num = Mathf.Max(1, ((Object)(object)_logic != (Object)null) ? _logic.numberOfOctaves : 6); int num2 = key / num; int num3 = key % num; if (value == PianoLabelMode.ClipName) { try { List<Sound> musicKeys = _logic.musicKeys; if (musicKeys != null && musicKeys.Count > 0) { int num4 = Mathf.Clamp(num2, 0, musicKeys.Count - 1); Sound val = musicKeys[num4]; if (val != null && ((object)val).GetType().GetField("Sounds", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(val) is Array array && array.Length > 0) { object value2 = array.GetValue(0); if (value2 != null) { string text = (value2.GetType().GetProperty("name", BindingFlags.Instance | BindingFlags.Public)?.GetValue(value2) as string) ?? value2.ToString(); if (!string.IsNullOrEmpty(text)) { return text; } } return $"Clip{num4}"; } } } catch { } return (key + 1).ToString(); } List<string> obj2 = ((_noteNames != null && _noteNames.Count > 0) ? _noteNames : ParseNoteNames(PianoHintsPlugin.NoteNamesCSV.Value)); int count = obj2.Count; int num5 = Mathf.Clamp(PianoHintsPlugin.NoteRootIndex.Value, 0, count - 1); int num6 = num2 + num5; string text2 = obj2[num6 % count]; if (value == PianoLabelMode.Note) { return text2; } int value3 = PianoHintsPlugin.BaseOctave.Value; int num7 = num6 / count; int num8 = Mathf.RoundToInt((float)num3 / Mathf.Max(1f, (float)num) * (float)count) / count; int num9 = value3 + num7 + num8; return $"{text2}{num9}"; } private void TryAddTextMesh(GameObject go, string text, float thickness) { //IL_00cb: Unknown result type (might be due to invalid IL or missing references) try { Type type = Type.GetType("UnityEngine.TextMesh, UnityEngine.TextRenderingModule") ?? Type.GetType("UnityEngine.TextMesh, UnityEngine") ?? null; if (type == null) { return; } Component val = go.AddComponent(type); _textComps.Add(val); PropertyInfo property = type.GetProperty("text"); PropertyInfo? property2 = type.GetProperty("color"); PropertyInfo? property3 = type.GetProperty("fontSize"); PropertyInfo property4 = type.GetProperty("characterSize"); PropertyInfo property5 = type.GetProperty("anchor"); PropertyInfo property6 = type.GetProperty("alignment"); property?.SetValue(val, text); property3?.SetValue(val, PianoHintsPlugin.FontSize.Value); property4?.SetValue(val, thickness * 0.5f); property2?.SetValue(val, Color.black); try { Type type2 = Type.GetType("UnityEngine.TextAnchor, UnityEngine.TextRenderingModule") ?? Type.GetType("UnityEngine.TextAnchor, UnityEngine") ?? null; if (type2 != null) { object value = Enum.Parse(type2, "MiddleCenter"); property5?.SetValue(val, value); } } catch { } try { Type type3 = Type.GetType("UnityEngine.TextAlignment, UnityEngine.TextRenderingModule") ?? Type.GetType("UnityEngine.TextAlignment, UnityEngine") ?? null; if (type3 != null) { object value2 = Enum.Parse(type3, "Center"); property6?.SetValue(val, value2); } } catch { } } catch (Exception ex) { ManualLogSource logS = PianoHintsPlugin.LogS; if (logS != null) { logS.LogWarning((object)("[TryAddTextMesh] " + ex.GetType().Name + ": " + ex.Message)); } } } } public class PianoHintBillboard : MonoBehaviour { private void LateUpdate() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) Camera main = Camera.main; if (!((Object)(object)main == (Object)null)) { Vector3 val = ((Component)main).transform.position - ((Component)this).transform.position; if (((Vector3)(ref val)).sqrMagnitude > 0.0001f) { Quaternion val2 = Quaternion.LookRotation(((Vector3)(ref val)).normalized, ((Component)main).transform.up); Quaternion val3 = Quaternion.Euler(PianoHintsPlugin.LabelRotX.Value, PianoHintsPlugin.LabelRotY.Value, PianoHintsPlugin.LabelRotZ.Value); ((Component)this).transform.rotation = val2 * val3; } } } } public class PianoSongTeacher : MonoBehaviour { private PianoHintsOverlay _overlay; private MusicalValuableLogic _logic; private bool _active; private List<int> _sequence = new List<int>(); private int _cursor; private Color _hlColor = new Color(1f, 0.82f, 0.125f, 0.95f); private string _lastSongId = ""; private int _lastStartKey; private void Awake() { _overlay = ((Component)this).GetComponent<PianoHintsOverlay>(); _logic = ((Component)this).GetComponent<MusicalValuableLogic>(); TryParseHighlightColor(); } private void Update() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) if (PianoHintsPlugin.TeacherStartKey == null) { return; } KeyboardShortcut value = PianoHintsPlugin.TeacherStartKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { if (!string.IsNullOrEmpty(_lastSongId)) { StopTeaching(); StartSongInternal(_lastSongId, _lastStartKey); } else { StopTeaching(); StartSongInternal(PianoHintsPlugin.TeacherSong.Value, PianoHintsPlugin.TeacherStartKeyIndex.Value); } } } private void TryParseHighlightColor() { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) try { string value = PianoHintsPlugin.TeacherHighlightColor.Value; if (!string.IsNullOrWhiteSpace(value)) { value = value.Trim(); if (value.StartsWith("#")) { value = value.Substring(1); } uint num = Convert.ToUInt32(value, 16); if (value.Length <= 6) { byte b = (byte)((num >> 16) & 0xFFu); byte b2 = (byte)((num >> 8) & 0xFFu); byte b3 = (byte)(num & 0xFFu); _hlColor = Color32.op_Implicit(new Color32(b, b2, b3, (byte)240)); } else { byte b4 = (byte)((num >> 24) & 0xFFu); byte b5 = (byte)((num >> 16) & 0xFFu); byte b6 = (byte)((num >> 8) & 0xFFu); byte b7 = (byte)(num & 0xFFu); _hlColor = Color32.op_Implicit(new Color32(b4, b5, b6, b7)); } } } catch { } } public void OnKeyPressed(int keyIndex) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) if (!_active || (Object)(object)_overlay == (Object)null || _cursor < 0 || _cursor >= _sequence.Count) { return; } int num = _sequence[_cursor]; if (keyIndex == num) { _overlay.ClearHighlight(num); _cursor++; if (_cursor < _sequence.Count) { _overlay.HighlightKey(_sequence[_cursor], _hlColor); } else { StopTeaching(); } } } private void StartTeaching() { //IL_0082: Unknown result type (might be due to invalid IL or missing references) _sequence = BuildSong(PianoHintsPlugin.TeacherSong.Value, PianoHintsPlugin.TeacherStartKeyIndex.Value); if ((Object)(object)_overlay == (Object)null || _sequence.Count == 0) { ManualLogSource logS = PianoHintsPlugin.LogS; if (logS != null) { logS.LogWarning((object)"[Teacher] Unable to start; missing overlay/logic or sequence."); } return; } ClampToKeyboard(_sequence); _cursor = 0; _active = true; _overlay.HighlightKey(_sequence[_cursor], _hlColor); ManualLogSource logS2 = PianoHintsPlugin.LogS; if (logS2 != null) { logS2.LogInfo((object)$"[Teacher] Started song: {PianoHintsPlugin.TeacherSong.Value} with {_sequence.Count} notes."); } } internal void StartSongExternal(string id, int startKey) { StopTeaching(); StartSongInternal(id, startKey); } private void StartSongInternal(string id, int startKey) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) _sequence = BuildSong(id, startKey); if ((Object)(object)_overlay == (Object)null || _sequence.Count == 0) { ManualLogSource logS = PianoHintsPlugin.LogS; if (logS != null) { logS.LogWarning((object)("[Teacher] Unable to start song '" + id + "'.")); } return; } ClampToKeyboard(_sequence); _cursor = 0; _active = true; _lastSongId = id; _lastStartKey = startKey; _overlay.HighlightKey(_sequence[_cursor], _hlColor); ManualLogSource logS2 = PianoHintsPlugin.LogS; if (logS2 != null) { logS2.LogInfo((object)$"[Teacher] Started song: {id} with {_sequence.Count} notes."); } } private void StopTeaching() { if ((Object)(object)_overlay != (Object)null) { foreach (int item in _sequence) { _overlay.ClearHighlight(item); } } _sequence.Clear(); _cursor = 0; _active = false; ManualLogSource logS = PianoHintsPlugin.LogS; if (logS != null) { logS.LogInfo((object)"[Teacher] Stopped."); } } private void ClampToKeyboard(List<int> seq) { int num = Mathf.Max(0, ((Object)(object)_overlay != (Object)null) ? _overlay.KeyCount : 0) - 1; for (int i = 0; i < seq.Count; i++) { seq[i] = Mathf.Clamp(seq[i], 0, num); } } private List<int> BuildSong(string id, int startKey) { List<int> list = new List<int>(); if (PianoHintsPlugin.TryGetSongOffsets(id, out List<int> offsets) && offsets != null && offsets.Count > 0) { foreach (int item in offsets) { list.Add(startKey + item); } return list; } if (string.IsNullOrEmpty(id) || id.Equals("happybirthday", StringComparison.OrdinalIgnoreCase)) { int[] array = new int[12] { 0, 0, 2, 0, 5, 4, 0, 0, 2, 0, 7, 5 }; foreach (int num in array) { list.Add(startKey + num); } return list; } return list; } } [HarmonyPatch(typeof(MusicalValuableLogic), "MusicKeyPressedRPC")] internal static class PianoHints_KeyPressedPatch { private static void Postfix(MusicalValuableLogic __instance, int keyIndex, int grabberID) { PianoSongTeacher component = ((Component)__instance).GetComponent<PianoSongTeacher>(); if ((Object)(object)component != (Object)null) { component.OnKeyPressed(keyIndex); } } } [HarmonyPatch(typeof(PlayerAvatar), "ChatMessageSend")] internal static class PianoHints_ChatCommandPatch { private static bool Prefix(string _message) { try { if (!string.IsNullOrWhiteSpace(_message) && _message.StartsWith("/song ", StringComparison.OrdinalIgnoreCase)) { string text = ((_message.Length > 5) ? _message.Substring(5).Trim() : PianoHintsPlugin.TeacherSong.Value); if (!string.IsNullOrWhiteSpace(text)) { PianoHintsPlugin.TryStartSongFromChat(text); } return false; } } catch { } return true; } }