using 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;
}
}