using 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.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Mirror; using UnityEngine; using UnityEngine.SceneManagement; using YAPYAP; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("SpellEditor")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("SpellEditor")] [assembly: AssemblyTitle("SpellEditor")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SpellEditor { [StructLayout(LayoutKind.Sequential, Size = 1)] public struct FlightRequestMessage : NetworkMessage { } public static class FlightCommand { [HarmonyPatch(typeof(NetworkManager), "OnStartServer")] public static class OnStartServerPatch { public static void Postfix() { RegisterHandlers(); } } [HarmonyPatch(typeof(NetworkManager), "OnStartClient")] public static class OnStartClientPatch { public static void Postfix() { RegisterHandlers(); } } [HarmonyPatch(typeof(PawnVoiceCommands), "Start")] public static class PawnVoiceCommandsStartPatch { public static void Postfix(PawnVoiceCommands __instance) { Pawn component = ((Component)__instance).GetComponent(); if (!((Object)(object)component == (Object)null) && ((NetworkBehaviour)component).isLocalPlayer && !((Object)(object)component.Voice == (Object)null)) { PawnVoice voice = component.Voice; voice.OnVoiceResult = (Action)Delegate.Combine(voice.OnVoiceResult, new Action(OnVoiceResult)); if ((Object)(object)UIManager.Instance?.uiChat != (Object)null) { UIManager.Instance.uiChat.OnMessageSendVoice += OnVoiceResult; } Plugin.Log.LogInfo((object)"[FlightCommand] Voice listener registered on local player."); } } } [HarmonyPatch(typeof(StatusFlying), "OnFixedUpdate")] public static class StatusFlyingLandingPatch { public static void Postfix(StatusFlying __instance) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) object? obj = _pawnField?.GetValue(__instance); Pawn val = (Pawn)((obj is Pawn) ? obj : null); if ((Object)(object)val == (Object)null || !((NetworkBehaviour)val).isServer) { return; } Rigidbody rigidbody = val.Rigidbody; if (!((Object)(object)rigidbody == (Object)null) && !(Time.time - _activatedTime < 3f) && !(rigidbody.linearVelocity.y > 0.5f) && Physics.Raycast(((Component)val).transform.position, Vector3.down, 2f)) { StatusFlying lastStatus = val.PawnStatus.GetLastStatus(); if (lastStatus != null) { val.PawnStatus.RemoveStatus((Status)(object)lastStatus); } } } } private const string LocKey = "PLAYER_COMMAND_BECOME_WIND"; private const float Duration = 60f; private static StatusFlyingSO? _statusSO; private static float _activatedTime = -999f; private const float GracePeriod = 3f; private static readonly FieldInfo _pawnField = typeof(Status).GetField("_pawn", BindingFlags.Instance | BindingFlags.NonPublic); private const float LandingRayDistance = 2f; public static void RegisterHandlers() { try { NetworkServer.RegisterHandler((Action)OnServerReceive, false); } catch { } try { NetworkClient.RegisterHandler((Action)OnClientReceive, false); } catch { } } private static void OnVoiceResult(string input) { if (string.IsNullOrEmpty(input) || !MatchesTrigger(input)) { return; } if (NetworkServer.active) { Pawn val = ((IEnumerable)Object.FindObjectsByType((FindObjectsSortMode)0)).FirstOrDefault((Func)((Pawn p) => ((NetworkBehaviour)p).isLocalPlayer)); if ((Object)(object)val != (Object)null) { Apply(val); } } else if (NetworkClient.isConnected) { NetworkClient.Send(default(FlightRequestMessage), 0); } } private static bool MatchesTrigger(string input) { string[] currentWords = VoiceInjector.GetCurrentWords("PLAYER_COMMAND_BECOME_WIND", (SystemLanguage)10); if (currentWords == null || currentWords.Length == 0) { return false; } int num = -1; string[] array = currentWords; foreach (string text in array) { int num2 = input.IndexOf(text, num + 1, StringComparison.OrdinalIgnoreCase); if (num2 < 0) { return false; } num = num2 + text.Length - 1; } return true; } private static void OnServerReceive(NetworkConnectionToClient conn, FlightRequestMessage msg) { NetworkIdentity identity = ((NetworkConnection)conn).identity; Pawn val = ((identity != null) ? ((Component)identity).GetComponent() : null); if ((Object)(object)val != (Object)null) { Apply(val); } } private static void OnClientReceive(FlightRequestMessage msg) { } private static void Apply(Pawn pawn) { if (NetworkServer.active) { StatusFlyingSO statusFlyingSO = GetStatusFlyingSO(); if ((Object)(object)statusFlyingSO == (Object)null) { Plugin.Log.LogWarning((object)"[FlightCommand] StatusFlyingSO not found in resources."); return; } _activatedTime = Time.time; pawn.PawnStatus.AddStatus((StatusSO)(object)statusFlyingSO, 60f, 0f); Plugin.Log.LogInfo((object)$"[FlightCommand] StatusFlying applied to {((Object)pawn).name} for {60f}s."); } } private static StatusFlyingSO? GetStatusFlyingSO() { if ((Object)(object)_statusSO != (Object)null) { return _statusSO; } _statusSO = Resources.FindObjectsOfTypeAll().FirstOrDefault(); return _statusSO; } } public class NetworkSync : MonoBehaviour { [HarmonyPatch(typeof(NetworkManager), "OnStartServer")] public static class ServerStartPatch { public static void Postfix() { TryRegisterHandlers(); } } [HarmonyPatch(typeof(NetworkManager), "OnStartClient")] public static class ClientStartPatch { public static void Postfix() { TryRegisterHandlers(); } } public static NetworkSync? Instance { get; private set; } private void Awake() { Instance = this; } private void Start() { TryRegisterHandlers(); } public static void TryRegisterHandlers() { try { NetworkServer.RegisterHandler((Action)OnServerReceive, false); } catch { } try { NetworkClient.RegisterHandler((Action)OnClientReceive, false); } catch { } } public void BroadcastFieldChange(NetworkPuppetWandProp wand, string fieldName, string value) { WandSpellFieldMessage wandSpellFieldMessage = default(WandSpellFieldMessage); wandSpellFieldMessage.WandNetId = WandRegistry.GetWandId(wand); wandSpellFieldMessage.FieldName = fieldName; wandSpellFieldMessage.FieldValue = value; WandSpellFieldMessage wandSpellFieldMessage2 = wandSpellFieldMessage; if (NetworkServer.active) { NetworkServer.SendToAll(wandSpellFieldMessage2, 0, false); } else if (NetworkClient.isConnected) { NetworkClient.Send(wandSpellFieldMessage2, 0); } else { ApplyFieldChange(wandSpellFieldMessage2); } } private static void ApplyFieldChange(WandSpellFieldMessage msg) { NetworkPuppetWandProp val = ((IEnumerable)WandRegistry.All).FirstOrDefault((Func)((NetworkPuppetWandProp w) => WandRegistry.GetWandId(w) == msg.WandNetId)); if ((Object)(object)val == (Object)null) { WandRegistry.Refresh(); val = ((IEnumerable)WandRegistry.All).FirstOrDefault((Func)((NetworkPuppetWandProp w) => WandRegistry.GetWandId(w) == msg.WandNetId)); } if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)("NetworkSync: wand '" + msg.WandNetId + "' not found.")); return; } Spell spell = val.Spell; SpellData val2 = ((spell != null) ? spell.SpellData : null); if ((Object)(object)val2 == (Object)null) { Plugin.Log.LogWarning((object)("NetworkSync: wand '" + msg.WandNetId + "' has no SpellData.")); return; } FieldInfo field = typeof(SpellData).GetField(msg.FieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { Plugin.Log.LogWarning((object)("NetworkSync: field '" + msg.FieldName + "' not found on SpellData.")); return; } try { object obj = SpellEditorWindow.ParseValue(field.FieldType, msg.FieldValue); if (obj != null) { field.SetValue(val2, obj); } Plugin.Log.LogInfo((object)("NetworkSync: applied " + msg.FieldName + "=" + msg.FieldValue + " to wand " + msg.WandNetId + ".")); } catch { Plugin.Log.LogWarning((object)("NetworkSync: could not parse '" + msg.FieldValue + "' for field '" + msg.FieldName + "'.")); } } private static void OnServerReceive(NetworkConnectionToClient conn, WandSpellFieldMessage msg) { ApplyFieldChange(msg); NetworkServer.SendToAll(msg, 0, false); } private static void OnClientReceive(WandSpellFieldMessage msg) { if (!NetworkServer.active) { ApplyFieldChange(msg); } } } public struct WandSpellFieldMessage : NetworkMessage { public string WandNetId; public string FieldName; public string FieldValue; } public static class OculusTempestVariant { [HarmonyPatch(typeof(ProjectileTornado), "Fire")] public static class OculusTornadoSpawnPatch { public static void Postfix(ProjectileTornado __instance) { object? obj = _ownerSpellField?.GetValue(__instance); object? obj2 = ((obj is Spell) ? obj : null); if (((obj2 == null) ? null : ((Spell)obj2).SpellData?.voiceCommandKey) == "SPELL_BAS_OCULUS_TEMPEST") { _oculusTornadoes.Add(__instance); } } } [HarmonyPatch(typeof(ProjectileTornado), "FixedUpdate")] public static class OculusTempestMovementPatch { public static void Postfix(ProjectileTornado __instance) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) if (!((NetworkBehaviour)__instance).isServer || !_oculusTornadoes.Contains(__instance)) { return; } object? obj = _casterField?.GetValue(__instance); NetworkIdentity val = (NetworkIdentity)((obj is NetworkIdentity) ? obj : null); if ((Object)(object)val == (Object)null) { return; } Pawn component = ((Component)val).GetComponent(); if (!((Object)(object)component == (Object)null)) { Vector3 aimDirection = component.Blackboard.AimDirection; aimDirection.y = 0f; if (!(((Vector3)(ref aimDirection)).sqrMagnitude < 0.01f)) { ((Vector3)(ref aimDirection)).Normalize(); _baseDirectionField?.SetValue(__instance, aimDirection); _targetDirectionField?.SetValue(__instance, aimDirection); _isChangingDirectionField?.SetValue(__instance, false); _nextDirectionChangeTimeField?.SetValue(__instance, Time.time + 9999f); } } } } [HarmonyPatch(typeof(ProjectileTornado), "OnDestroy")] public static class OculusTornadoDestroyPatch { public static void Postfix(ProjectileTornado __instance) { _oculusTornadoes.Remove(__instance); } } private static readonly BindingFlags BF = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static readonly FieldInfo _baseDirectionField = typeof(ProjectileTornado).GetField("baseDirection", BF); private static readonly FieldInfo _targetDirectionField = typeof(ProjectileTornado).GetField("targetDirection", BF); private static readonly FieldInfo _nextDirectionChangeTimeField = typeof(ProjectileTornado).GetField("nextDirectionChangeTime", BF); private static readonly FieldInfo _isChangingDirectionField = typeof(ProjectileTornado).GetField("isChangingDirection", BF); private static readonly FieldInfo _casterField = typeof(BaseProjectile).GetField("Caster", BF); private const string OculusKey = "SPELL_BAS_OCULUS_TEMPEST"; private static readonly FieldInfo _ownerSpellField = typeof(BaseProjectile).GetField("OwnerSpell", BF); private static readonly HashSet _oculusTornadoes = new HashSet(); public static void RegisterProjectilePrefab(GameObject prefab) { } } [BepInPlugin("com.local.yapyap.SpellEditor", "SpellEditor", "1.0.1")] public class Plugin : BaseUnityPlugin { public const string GUID = "com.local.yapyap.SpellEditor"; public const string Name = "SpellEditor"; public const string Version = "1.0.1"; private static readonly Dictionary RequiredVoiceEntries = new Dictionary { { "SPELL_BAS_FEATHER_FALL", "FEATHER-FALL" }, { "SPELL_BAS_AERO_MAXIMA", "FUS" }, { "SPELL_BAS_OCULUS_TEMPEST", "OCULUS-TEMPEST" }, { "PLAYER_COMMAND_BECOME_WIND", "LET-GO-YOUR-EARTHLY-TETHER-ENTER-THE-VOID-EMPTY-AND-BECOME-WIND" } }; internal static ManualLogSource Log { get; private set; } = null; private static void PatchEnglishTxt() { string text = Path.Combine(Application.dataPath, "StreamingAssets", "Vosk", "Localisation", "english.txt"); if (!File.Exists(text)) { Log.LogWarning((object)("[SpellEditor] english.txt not found at " + text)); return; } List list = new List(File.ReadAllLines(text)); bool flag = false; foreach (KeyValuePair requiredVoiceEntry in RequiredVoiceEntries) { string key = requiredVoiceEntry.Key; string value = requiredVoiceEntry.Value; string text2 = key + " :: " + value; bool flag2 = false; foreach (string item in list) { if (item.StartsWith(key + " ::")) { flag2 = true; break; } } if (!flag2) { list.Add(text2); flag = true; Log.LogInfo((object)("[SpellEditor] Added voice entry: " + text2)); } } if (flag) { File.WriteAllLines(text, list); } else { Log.LogInfo((object)"[SpellEditor] english.txt already up to date."); } } private void Awake() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; PatchEnglishTxt(); new Harmony("com.local.yapyap.SpellEditor").PatchAll(Assembly.GetExecutingAssembly()); FlightCommand.RegisterHandlers(); SpellVariants.RegisterTriggers(); VoiceInjector.SetOverride("PLAYER_COMMAND_BECOME_WIND", (SystemLanguage)10, VoiceInjector.ParseInput("LET-GO-YOUR-EARTHLY-TETHER-ENTER-THE-VOID-EMPTY-AND-BECOME-WIND")); VoiceInjector.SetOverride("SPELL_BAS_OCULUS_TEMPEST", (SystemLanguage)10, VoiceInjector.ParseInput("OCULUS-TEMPEST")); VoiceInjector.SetOverride("SPELL_BAS_FEATHER_FALL", (SystemLanguage)10, VoiceInjector.ParseInput("FEATHER-FALL")); VoiceInjector.SetOverride("SPELL_BAS_AERO_MAXIMA", (SystemLanguage)10, VoiceInjector.ParseInput("FUS")); GameObject val = new GameObject("SpellEditorRoot"); Object.DontDestroyOnLoad((Object)val); val.AddComponent(); val.AddComponent(); Log.LogInfo((object)"SpellEditor 1.0.1 loaded. Press F8 to open."); } } public static class PrefabInjector { [HarmonyPatch(typeof(UpdraftSpell), "Update")] public static class FeatherFallLandingPatch { public static void Postfix(UpdraftSpell __instance) { //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) if (((Spell)__instance).SpellData?.voiceCommandKey != "SPELL_BAS_FEATHER_FALL") { return; } object obj = _isUpdraftActiveField?.GetValue(__instance); if (!(obj is bool) || !(bool)obj) { return; } object? obj2 = _casterPawnField?.GetValue(__instance); Pawn val = (Pawn)((obj2 is Pawn) ? obj2 : null); if ((Object)(object)val == (Object)null || !((NetworkBehaviour)val).isLocalPlayer) { return; } float num2 = ((_updraftStartTimeField?.GetValue(__instance) is float num) ? num : 0f); if (!(Time.time - num2 < 3f)) { object? obj3 = _getActiveRigidbodyMethod?.Invoke(__instance, null); Rigidbody val2 = (Rigidbody)((obj3 is Rigidbody) ? obj3 : null); if (!((Object)(object)val2 == (Object)null) && !(val2.linearVelocity.y > 0.5f) && Physics.Raycast(((Component)__instance).transform.position, Vector3.down, 2f)) { _stopUpdraftMethod?.Invoke(__instance, null); } } } } [HarmonyPatch(typeof(VoiceSpell), "CollectHeldSpells")] public static class CollectHeldSpellsPatch { public static void Postfix(VoiceSpell __instance) { if (_allSpellsField?.GetValue(__instance) is List allSpells) { TryAdd(__instance, allSpells, (Spell?)(object)((IEnumerable)((Component)__instance).GetComponents()).FirstOrDefault((Func)((UpdraftSpell u) => ((Spell)u).SpellData?.voiceCommandKey == "SPELL_BAS_FEATHER_FALL"))); TryAdd(__instance, allSpells, ((IEnumerable)((Component)__instance).GetComponents()).FirstOrDefault((Func)((Spell s) => ((s == null) ? null : s.SpellData?.voiceCommandKey) == "SPELL_BAS_AERO_MAXIMA"))); TryAdd(__instance, allSpells, ((IEnumerable)((Component)__instance).GetComponents()).FirstOrDefault((Func)((Spell s) => ((s == null) ? null : s.SpellData?.voiceCommandKey) == "SPELL_BAS_OCULUS_TEMPEST"))); } } private static void TryAdd(VoiceSpell vs, List allSpells, Spell? spell) { if ((Object)(object)spell == (Object)null || allSpells.Contains(spell)) { return; } if (!spell.IsInitialized) { spell.Initialize(((Spell)vs).CasterIdentity); if (NetworkServer.active) { spell.SetWandHeld(true); } } allSpells.Add(spell); _sortSpells?.Invoke(vs, null); } } [HarmonyPatch(typeof(Spell), "CanCast")] public static class FeatherFallCanCastPatch { public static bool Prefix(Spell __instance, ref bool __result) { if (__instance.SpellData?.voiceCommandKey != "SPELL_BAS_FEATHER_FALL") { return true; } UpdraftSpell val = FindParentUpDog((Component)(object)__instance); if ((Object)(object)val == (Object)null) { return true; } __result = ((Spell)val).CanCast(); return false; } } [HarmonyPatch(typeof(UpdraftSpell), "OnSpellActivate")] public static class FeatherFallActivatePatch { public static void Postfix(UpdraftSpell __instance) { if (!(((Spell)__instance).SpellData?.voiceCommandKey != "SPELL_BAS_FEATHER_FALL") && NetworkServer.active) { UpdraftSpell? obj = FindParentUpDog((Component)(object)__instance); if (obj != null) { ((Spell)obj).SetCastData(); } } } } [HarmonyPatch(typeof(Spell), "OnSpellDeactivate")] public static class FeatherFallDeactivatePatch { public static void Postfix(Spell __instance) { if (!(__instance.SpellData?.voiceCommandKey != "SPELL_BAS_FEATHER_FALL") && NetworkServer.active) { UpdraftSpell? obj = FindParentUpDog((Component)(object)__instance); if (obj != null) { ((Spell)obj).OnSpellDeactivate(); } } } } [HarmonyPatch(typeof(Spell), "CanCast")] public static class AeroMaximaCanCastPatch { public static bool Prefix(Spell __instance, ref bool __result) { if (__instance.SpellData?.voiceCommandKey != "SPELL_BAS_AERO_MAXIMA") { return true; } Spell val = FindParentAero((Component)(object)__instance); if ((Object)(object)val == (Object)null) { return true; } __result = val.CanCast() && val.CurrentCharges >= 2; return false; } } [HarmonyPatch(typeof(Spell), "OnSpellActivate")] public static class AeroMaximaActivatePatch { public static void Postfix(Spell __instance) { if (__instance.SpellData?.voiceCommandKey != "SPELL_BAS_AERO_MAXIMA" || !NetworkServer.active) { return; } Spell val = FindParentAero((Component)(object)__instance); if ((Object)(object)val == (Object)null) { return; } val.SetCastData(); val.SetCastData(); if (Time.time - _lastYellTime < 1f) { return; } _lastYellTime = Time.time; NetworkIdentity casterIdentity = __instance.CasterIdentity; Pawn val2 = ((casterIdentity != null) ? ((Component)casterIdentity).GetComponent() : null); if ((Object)(object)((val2 != null) ? val2.Emotes : null) != (Object)null) { object? obj = ((object)val2.Emotes).GetType().GetField("emoteState", BF)?.GetValue(val2.Emotes); object? obj2 = ((obj is ControllerStateEmote) ? obj : null); if (obj2 != null) { ((ControllerStateEmote)obj2).PlayEmote(_yellEmoteHash, 1.5f); } } } } [HarmonyPatch(typeof(Spell), "OnSpellDeactivate")] public static class AeroMaximaDeactivatePatch { public static void Postfix(Spell __instance) { if (!(__instance.SpellData?.voiceCommandKey != "SPELL_BAS_AERO_MAXIMA") && NetworkServer.active) { Spell? obj = FindParentAero((Component)(object)__instance); if (obj != null) { obj.OnSpellDeactivate(); } } } } [HarmonyPatch(typeof(Spell), "CanCast")] public static class OculusTempestCanCastPatch { public static bool Prefix(Spell __instance, ref bool __result) { if (__instance.SpellData?.voiceCommandKey != "SPELL_BAS_OCULUS_TEMPEST") { return true; } Spell val = FindParentTempest((Component)(object)__instance); if ((Object)(object)val == (Object)null) { return true; } __result = val.CanCast(); return false; } } [HarmonyPatch(typeof(Spell), "OnSpellActivate")] public static class OculusTempestActivatePatch { public static void Postfix(Spell __instance) { if (!(__instance.SpellData?.voiceCommandKey != "SPELL_BAS_OCULUS_TEMPEST") && NetworkServer.active) { Spell? obj = FindParentTempest((Component)(object)__instance); if (obj != null) { obj.SetCastData(); } } } } [HarmonyPatch(typeof(Spell), "OnSpellDeactivate")] public static class OculusTempestDeactivatePatch { public static void Postfix(Spell __instance) { if (!(__instance.SpellData?.voiceCommandKey != "SPELL_BAS_OCULUS_TEMPEST") && NetworkServer.active) { Spell? obj = FindParentTempest((Component)(object)__instance); if (obj != null) { obj.OnSpellDeactivate(); } } } } [HarmonyPatch(typeof(NetworkManager), "OnStartServer")] public static class OnStartServerPatch { public static void Postfix() { Inject(); } } [HarmonyPatch(typeof(NetworkManager), "OnStartClient")] public static class OnStartClientPatch { public static void Postfix() { Inject(); } } private static bool _done; private static readonly BindingFlags BF = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static readonly FieldInfo _spellDataField = typeof(Spell).GetField("spellData", BF); private static readonly FieldInfo _isSecretField = typeof(Spell).GetField("isSecret", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo _allSpellsField = typeof(VoiceSpell).GetField("allSpells", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly MethodInfo _sortSpells = typeof(VoiceSpell).GetMethod("SortSpells", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo _isUpdraftActiveField = typeof(UpdraftSpell).GetField("isUpdraftActive", BF); private static readonly FieldInfo _updraftStartTimeField = typeof(UpdraftSpell).GetField("updraftStartTime", BF); private static readonly FieldInfo _casterPawnField = typeof(Spell).GetField("casterPawn", BF); private static readonly MethodInfo _stopUpdraftMethod = typeof(UpdraftSpell).GetMethod("StopUpdraft", BF); private static readonly MethodInfo _getActiveRigidbodyMethod = typeof(UpdraftSpell).GetMethod("GetActiveRigidbody", BF); private const float FeatherFallGracePeriod = 3f; private const float FeatherFallLandingRayDist = 2f; private static readonly int _yellEmoteHash = Animator.StringToHash("Emote_Yell"); private static float _lastYellTime = -999f; private static UpdraftSpell? FindParentUpDog(Component feather) { return Array.Find(feather.GetComponents(), (UpdraftSpell u) => ((Spell)u).SpellData?.voiceCommandKey == "SPELL_BAS_UPDOG"); } private static Spell? FindParentAero(Component variant) { return Array.Find(variant.GetComponents(), (Spell s) => ((s == null) ? null : s.SpellData?.voiceCommandKey) == "SPELL_BAS_VENTO"); } private static Spell? FindParentTempest(Component variant) { return Array.Find(variant.GetComponents(), (Spell s) => ((s == null) ? null : s.SpellData?.voiceCommandKey) == "SPELL_BAS_TEMPEST"); } public static void Inject() { if (!_done) { NetworkPuppetWandProp val = FindWandPrefab(); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"[PrefabInjector] PropWandOfBasics prefab not found."); return; } InjectFeatherFall(val); InjectAeroMaxima(val); InjectOculusTempest(val); _done = true; } } private static void InjectFeatherFall(NetworkPuppetWandProp wand) { UpdraftSpell[] components = ((Component)wand).GetComponents(); if (components.Length < 2) { UpdraftSpell val = components.FirstOrDefault(); if ((Object)(object)((val != null) ? ((Spell)val).SpellData : null) == (Object)null) { Plugin.Log.LogWarning((object)"[PrefabInjector] Feather-Fall: source UpdraftSpell missing."); return; } SpellData val2 = SpellRegistry.Clone(((Spell)val).SpellData, "FEATHER-FALL"); SetSdField(val2, "voiceCommandKey", "SPELL_BAS_FEATHER_FALL"); UpdraftSpell val3 = ((Component)wand).gameObject.AddComponent(); CopySerializedFields(typeof(UpdraftSpell), val, val3); _spellDataField?.SetValue(val3, val2); _isSecretField?.SetValue(val3, true); SetCompField((Component)(object)val3, "initialUpwardForce", 0f); SetCompField((Component)(object)val3, "continuousUpwardForce", 0f); SetCompField((Component)(object)val3, "fallSlowMultiplier", 0.88f); SetCompField((Component)(object)val3, "duration", 10f); ((Behaviour)val3).enabled = true; Plugin.Log.LogInfo((object)"[PrefabInjector] Feather-Fall added."); } } private static void InjectOculusTempest(NetworkPuppetWandProp wand) { Spell[] components = ((Component)wand).GetComponents(); if (Array.Exists(components, (Spell s) => ((s == null) ? null : s.SpellData?.voiceCommandKey) == "SPELL_BAS_OCULUS_TEMPEST")) { return; } Spell val = Array.Find(components, (Spell s) => ((s == null) ? null : s.SpellData?.voiceCommandKey) == "SPELL_BAS_TEMPEST"); if ((Object)(object)((val != null) ? val.SpellData : null) == (Object)null) { Plugin.Log.LogWarning((object)"[PrefabInjector] Oculus-Tempest: source Tempest spell missing."); return; } SpellData val2 = SpellRegistry.Clone(val.SpellData, "OCULUS-TEMPEST"); SetSdField(val2, "voiceCommandKey", "SPELL_BAS_OCULUS_TEMPEST"); GameObject val3 = CloneOculusProjectile(val.SpellData.objectToSpawnPrefab); if ((Object)(object)val3 != (Object)null) { SetSdField(val2, "objectToSpawnPrefab", val3); } Component obj = ((Component)wand).gameObject.AddComponent(((object)val).GetType()); Spell val4 = (Spell)(object)((obj is Spell) ? obj : null); if ((Object)(object)val4 == (Object)null) { Plugin.Log.LogWarning((object)"[PrefabInjector] Oculus-Tempest: AddComponent failed."); return; } CopySerializedFields(((object)val).GetType(), val, val4); _spellDataField?.SetValue(val4, val2); _isSecretField?.SetValue(val4, true); ((Behaviour)val4).enabled = true; Plugin.Log.LogInfo((object)"[PrefabInjector] Oculus-Tempest added."); } private static GameObject? CloneOculusProjectile(GameObject? original) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown if ((Object)(object)original == (Object)null) { return null; } GameObject val = new GameObject("OculusTempestProjectileContainer"); val.SetActive(false); Object.DontDestroyOnLoad((Object)(object)val); GameObject obj = Object.Instantiate(original, val.transform); OculusTempestVariant.RegisterProjectilePrefab(obj); return obj; } private static void InjectAeroMaxima(NetworkPuppetWandProp wand) { Spell[] components = ((Component)wand).GetComponents(); if (Array.Exists(components, (Spell s) => ((s == null) ? null : s.SpellData?.voiceCommandKey) == "SPELL_BAS_AERO_MAXIMA")) { return; } Spell val = Array.Find(components, (Spell s) => ((s == null) ? null : s.SpellData?.voiceCommandKey) == "SPELL_BAS_VENTO"); if ((Object)(object)((val != null) ? val.SpellData : null) == (Object)null) { Plugin.Log.LogWarning((object)"[PrefabInjector] Aero-Maxima: source Aero spell missing."); return; } SpellData val2 = SpellRegistry.Clone(val.SpellData, "AERO-MAXIMA"); SetSdField(val2, "voiceCommandKey", "SPELL_BAS_AERO_MAXIMA"); GameObject val3 = CloneProjectilePrefab(val.SpellData.objectToSpawnPrefab); if ((Object)(object)val3 != (Object)null) { SetSdField(val2, "objectToSpawnPrefab", val3); } Component obj = ((Component)wand).gameObject.AddComponent(((object)val).GetType()); Spell val4 = (Spell)(object)((obj is Spell) ? obj : null); if ((Object)(object)val4 == (Object)null) { Plugin.Log.LogWarning((object)"[PrefabInjector] Aero-Maxima: AddComponent failed."); return; } CopySerializedFields(((object)val).GetType(), val, val4); _spellDataField?.SetValue(val4, val2); _isSecretField?.SetValue(val4, true); ((Behaviour)val4).enabled = true; Plugin.Log.LogInfo((object)"[PrefabInjector] Aero-Maxima added."); } private static GameObject? CloneProjectilePrefab(GameObject? original) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)original == (Object)null) { return null; } GameObject val = new GameObject("AeroMaximaProjectileContainer"); val.SetActive(false); Object.DontDestroyOnLoad((Object)(object)val); GameObject obj = Object.Instantiate(original, val.transform); obj.transform.localScale = original.transform.localScale * 1.5f; ProjectilePush component = obj.GetComponent(); if ((Object)(object)component != (Object)null) { SetCompField((Component)(object)component, "pushForce", 340f); SetCompField((Component)(object)component, "playerPushForceMultiplier", 2.4f); SetCompField((Component)(object)component, "playerPushLiftMultiplier", 2f); SetCompField((Component)(object)component, "npcPushForceMultiplier", 4f); SetCompField((Component)(object)component, "maxTravelDistanceOverride", 15f); SetCompField((Component)(object)component, "decreaseForceByDistance", false); return obj; } Plugin.Log.LogWarning((object)"[PrefabInjector] Aero-Maxima: ProjectilePush not found on clone."); return obj; } private static void CopySerializedFields(Type type, object source, object dest) { FieldInfo[] fields = type.GetFields(BF); foreach (FieldInfo fieldInfo in fields) { if (!fieldInfo.IsStatic && (fieldInfo.IsPublic || fieldInfo.GetCustomAttributes(typeof(SerializeField), inherit: true).Length != 0)) { fieldInfo.SetValue(dest, fieldInfo.GetValue(source)); } } } private static NetworkPuppetWandProp? FindWandPrefab() { return Array.Find(Resources.FindObjectsOfTypeAll(), delegate(NetworkPuppetWandProp w) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (((Object)w).name == "PropWandOfBasics") { Scene scene = ((Component)w).gameObject.scene; return !((Scene)(ref scene)).IsValid(); } return false; }); } private static void SetSdField(SpellData sd, string name, object value) { typeof(SpellData).GetField(name, BF)?.SetValue(sd, value); } private static void SetCompField(Component comp, string name, object value) { ((object)comp).GetType().GetField(name, BF)?.SetValue(comp, value); } } public class SpellEditorWindow : MonoBehaviour { private bool _visible; private Rect _windowRect = new Rect(20f, 20f, 1100f, 680f); private int _mainTab; private int _detailTab; private string _search = ""; private string _schoolFilter = "ALL"; private Vector2 _listScroll; private int _selectedIndex = -1; private List _allSpells = new List(); private List _filtered = new List(); private Dictionary _keyTriggers = new Dictionary(); private List<(FieldInfo field, string draft, string live)> _fields = new List<(FieldInfo, string, string)>(); private Vector2 _detailScroll; private string _editKey = ""; private string _editTrigger = ""; private static readonly Dictionary Schools = new Dictionary { ["ARC"] = new Color32((byte)153, (byte)102, (byte)230, byte.MaxValue), ["BAS"] = new Color32((byte)102, (byte)204, (byte)102, byte.MaxValue), ["BLS"] = new Color32((byte)230, (byte)204, (byte)76, byte.MaxValue), ["FIR"] = new Color32((byte)230, (byte)102, (byte)51, byte.MaxValue), ["GRS"] = new Color32((byte)179, (byte)230, (byte)51, byte.MaxValue), ["ICE"] = new Color32((byte)102, (byte)204, (byte)230, byte.MaxValue), ["ILU"] = new Color32((byte)230, (byte)102, (byte)179, byte.MaxValue), ["TEL"] = new Color32((byte)230, (byte)153, (byte)51, byte.MaxValue), ["WIP"] = new Color32((byte)153, (byte)153, (byte)153, byte.MaxValue) }; private static readonly string[] SchoolKeys = new string[10] { "ALL", "ARC", "BAS", "BLS", "FIR", "GRS", "ICE", "ILU", "TEL", "WIP" }; private bool _stylesReady; private GUIStyle _winStyle; private GUIStyle _header; private GUIStyle _subLabel; private GUIStyle _fieldVal; private GUIStyle _tab; private GUIStyle _tabActive; private GUIStyle _row; private GUIStyle _rowSel; private Texture2D _txDark; private Texture2D _txPanel; private Texture2D _txSel; private Texture2D _txBtn; private Texture2D _txBtnHov; private void Update() { if (Input.GetKeyDown((KeyCode)289)) { _visible = !_visible; if (_visible) { Refresh(); } } } private void OnGUI() { //IL_0026: 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_004c: Expected O, but got Unknown //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) if (_visible) { EnsureStyles(); GUI.skin.label.richText = true; _windowRect = GUILayout.Window(47001, _windowRect, new WindowFunction(DrawWindow), "", _winStyle, Array.Empty()); } } private void Refresh() { SpellRegistry.Refresh(); WandRegistry.Refresh(); _keyTriggers = VoiceInjector.GetAllKeysWithTriggers((SystemLanguage)10).ToDictionary<(string, string), string, string>(((string key, string trigger) e) => e.key, ((string key, string trigger) e) => e.trigger); _allSpells = SpellRegistry.All.Where((SpellData s) => !string.IsNullOrEmpty(s.voiceCommandKey)).ToList(); ApplyFilter(); if (_selectedIndex >= _filtered.Count) { SelectSpell(-1); } } private void ApplyFilter() { _filtered = _allSpells.Where(delegate(SpellData s) { bool num = _schoolFilter == "ALL" || (s.voiceCommandKey?.Contains("_" + _schoolFilter + "_") ?? false); int num2; if (!string.IsNullOrEmpty(_search)) { string spellName = s.spellName; if (spellName == null || spellName.IndexOf(_search, StringComparison.OrdinalIgnoreCase) < 0) { string voiceCommandKey = s.voiceCommandKey; num2 = ((voiceCommandKey != null && voiceCommandKey.IndexOf(_search, StringComparison.OrdinalIgnoreCase) >= 0) ? 1 : 0); goto IL_008c; } } num2 = 1; goto IL_008c; IL_008c: bool flag = (byte)num2 != 0; return num && flag; }).ToList(); if (_selectedIndex >= _filtered.Count) { SelectSpell(-1); } } private void SelectSpell(int index) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) _selectedIndex = index; _detailScroll = Vector2.zero; if (index < 0 || index >= _filtered.Count) { _fields = new List<(FieldInfo, string, string)>(); _editKey = (_editTrigger = ""); return; } SpellData val = _filtered[index]; _editKey = val.voiceCommandKey ?? ""; _editTrigger = (_keyTriggers.TryGetValue(_editKey, out string value) ? value : ""); LoadFields(val); } private void LoadFields(SpellData sd) { SpellData sd2 = sd; _fields = (from f in typeof(SpellData).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where IsEditable(f.FieldType) && f.Name != "spellName" && f.Name != "voiceCommandKey" && f.Name != "remainingCooldownAlpha" select f).Select(delegate(FieldInfo f) { string text = f.GetValue(sd2)?.ToString() ?? ""; return (f, text, text); }).ToList(); } private void DrawWindow(int id) { //IL_0111: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("SpellEditor v2.0 · F8 to toggle", _header, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label($"{SpellRegistry.All.Count} spells · {WandRegistry.All.Count} wands", _subLabel, Array.Empty()); GUILayout.Space(8f); if (GUILayout.Button("Refresh", _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { Refresh(); } GUILayout.EndHorizontal(); GUILayout.Space(6f); GUILayout.BeginHorizontal(Array.Empty()); DrawMainTab(0, " Spells "); DrawMainTab(1, " Variants "); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); DrawSeparator(); GUILayout.Space(4f); if (_mainTab == 0) { DrawSpellsTab(); } else { DrawVariantsTab(); } GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, 30f)); } private void DrawMainTab(int index, string label) { if (GUILayout.Button(label, (_mainTab == index) ? _tabActive : _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) }) && _mainTab != index) { _mainTab = index; } } private void DrawSpellsTab() { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(290f) }); DrawBrowser(); GUILayout.EndVertical(); GUILayout.Space(10f); GUILayout.BeginVertical(Array.Empty()); DrawDetail(); GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private void DrawBrowser() { //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0195: 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_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty()); _ = GUI.skin.textField.normal.background; string text = GUILayout.TextField(_search, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (text != _search) { _search = text; ApplyFilter(); } GUILayout.EndHorizontal(); GUILayout.Space(4f); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); string[] schoolKeys = SchoolKeys; Color32 val = default(Color32); foreach (string text2 in schoolKeys) { if (_schoolFilter == text2) { val = (Color32)(Schools.TryGetValue(text2, out var value) ? value : new Color32((byte)80, (byte)120, (byte)220, byte.MaxValue)); } else { ((Color32)(ref val))..ctor((byte)50, (byte)50, (byte)58, byte.MaxValue); } Color backgroundColor = GUI.backgroundColor; GUI.backgroundColor = Color32.op_Implicit(val); if (GUILayout.Button(text2, _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width((float)((text2 == "ALL") ? 34 : 30)) })) { _schoolFilter = text2; ApplyFilter(); } GUI.backgroundColor = backgroundColor; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Space(4f); GUILayout.Label($"{_filtered.Count} spells", _subLabel, Array.Empty()); GUILayout.Space(2f); _listScroll = GUILayout.BeginScrollView(_listScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); for (int j = 0; j < _filtered.Count; j++) { SpellData val2 = _filtered[j]; string school = GetSchool(val2.voiceCommandKey); string value2; string text3 = (_keyTriggers.TryGetValue(val2.voiceCommandKey ?? "", out value2) ? value2 : "?"); Color32 value3; string text4 = (Schools.TryGetValue(school, out value3) ? $"#{value3.r:X2}{value3.g:X2}{value3.b:X2}" : "#888"); if (GUILayout.Button("" + val2.spellName + "\n" + text3 + "", (j == _selectedIndex) ? _rowSel : _row, Array.Empty())) { SelectSpell(j); } } GUILayout.EndScrollView(); } private void DrawDetail() { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) if (_selectedIndex < 0 || _selectedIndex >= _filtered.Count) { GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label("← select a spell", _subLabel, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.FlexibleSpace(); return; } SpellData val = _filtered[_selectedIndex]; string school = GetSchool(val.voiceCommandKey); Color32 value; string text = (Schools.TryGetValue(school, out value) ? $"#{value.r:X2}{value.g:X2}{value.b:X2}" : "#888"); GUILayout.Label("" + val.spellName + " " + school + " " + val.voiceCommandKey + "", _header, Array.Empty()); GUILayout.Space(4f); GUILayout.BeginHorizontal(Array.Empty()); DrawDetailTab(0, " Fields "); DrawDetailTab(1, " Voice "); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); DrawSeparator(); GUILayout.Space(4f); _detailScroll = GUILayout.BeginScrollView(_detailScroll, Array.Empty()); if (_detailTab == 0) { DrawFieldsDetail(val); } else { DrawVoiceDetail(); } GUILayout.EndScrollView(); } private void DrawDetailTab(int index, string label) { if (GUILayout.Button(label, (_detailTab == index) ? _tabActive : _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(22f) }) && _detailTab != index) { _detailTab = index; } } private void DrawFieldsDetail(SpellData sd) { //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0135: 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_014a: 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_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Field", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); GUILayout.Label("Current", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); GUILayout.Label("New value", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.EndHorizontal(); DrawSeparator(); GUILayout.Space(2f); for (int i = 0; i < _fields.Count; i++) { (FieldInfo field, string draft, string live) tuple = _fields[i]; FieldInfo item = tuple.field; string item2 = tuple.draft; string item3 = tuple.live; bool flag = item2 != item3; GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(item.Name, _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); Color color = GUI.color; GUI.color = new Color(1f, 1f, 1f, 0.35f); GUILayout.Label(item3, _fieldVal, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); GUI.color = color; Color backgroundColor = GUI.backgroundColor; if (flag) { GUI.backgroundColor = new Color(0.25f, 0.45f, 0.25f); } string text = GUILayout.TextField(item2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUI.backgroundColor = backgroundColor; if (text != item2) { _fields[i] = (item, text, item3); } GUILayout.EndHorizontal(); } GUILayout.Space(10f); bool num = _fields.Any<(FieldInfo, string, string)>(((FieldInfo field, string draft, string live) f) => f.draft != f.live); Color backgroundColor2 = GUI.backgroundColor; GUI.backgroundColor = (num ? new Color(0.25f, 0.6f, 0.25f) : new Color(0.3f, 0.3f, 0.3f)); GUI.enabled = num; if (GUILayout.Button("Apply Changes", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) })) { ApplyFields(sd); LoadFields(sd); } GUI.enabled = true; GUI.backgroundColor = backgroundColor2; } private void DrawVoiceDetail() { GUILayout.Label("Voice Key internal command identifier", _subLabel, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); _editKey = GUILayout.TextField(_editKey, Array.Empty()); if (GUILayout.Button("Assign", _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { SpellData sd = _filtered[_selectedIndex]; AssignKey(sd, _editKey); } GUILayout.EndHorizontal(); GUILayout.Space(12f); GUILayout.Label("Trigger what you say — hyphens or spaces", _subLabel, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); _editTrigger = GUILayout.TextField(_editTrigger, Array.Empty()); if (GUILayout.Button("Apply", _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { ApplyTrigger(_editKey, _editTrigger); } GUILayout.EndHorizontal(); } private void DrawVariantsTab() { GUILayout.Label("Runtime Spell Variants injected when wands spawn · removed when mod is unloaded", _header, Array.Empty()); DrawSeparator(); GUILayout.Space(6f); GUILayout.Label("No variants defined.", _subLabel, Array.Empty()); } private void AssignKey(SpellData sd, string newKey) { typeof(SpellData).GetField("voiceCommandKey", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.SetValue(sd, newKey); _editTrigger = (_keyTriggers.TryGetValue(newKey, out string value) ? value : ""); Plugin.Log.LogInfo((object)("[SpellEditor] Assigned key '" + newKey + "' to '" + sd.spellName + "'.")); } private void ApplyTrigger(string key, string input) { if (!string.IsNullOrWhiteSpace(key)) { string[] array = VoiceInjector.ParseInput(input); VoiceInjector.SetOverride(key, (SystemLanguage)10, array); _keyTriggers[key] = VoiceInjector.SyllablesToDisplay(array); Plugin.Log.LogInfo((object)("[SpellEditor] Trigger '" + key + "' → '" + string.Join("-", array) + "'.")); } } private void ApplyFields(SpellData sd) { foreach (var (fieldInfo, text, _) in _fields) { try { object obj = ParseValue(fieldInfo.FieldType, text); if (obj != null) { fieldInfo.SetValue(sd, obj); } } catch { Plugin.Log.LogWarning((object)("[SpellEditor] Could not parse '" + text + "' for '" + fieldInfo.Name + "'.")); } } Plugin.Log.LogInfo((object)("[SpellEditor] Fields applied to '" + sd.spellName + "'.")); } private static string GetSchool(string? key) { if (key == null) { return "???"; } string[] array = key.Split(new char[1] { '_' }); if (array.Length < 2) { return "???"; } return array[1]; } private void DrawSeparator() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) Color color = GUI.color; GUI.color = new Color(1f, 1f, 1f, 0.1f); GUILayout.Box("", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(1f) }); GUI.color = color; } private static bool IsEditable(Type t) { if (!(t == typeof(string)) && !(t == typeof(int)) && !(t == typeof(float)) && !(t == typeof(bool))) { return t == typeof(double); } return true; } public static object? ParseValue(Type t, string s) { if (t == typeof(string)) { return s; } if (t == typeof(int) && int.TryParse(s, out var result)) { return result; } if (t == typeof(float) && float.TryParse(s, out var result2)) { return result2; } if (t == typeof(double) && double.TryParse(s, out var result3)) { return result3; } if (t == typeof(bool) && bool.TryParse(s, out var result4)) { return result4; } return null; } private void EnsureStyles() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0055: 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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown //IL_00c2: Expected O, but got Unknown //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Expected O, but got Unknown //IL_0136: Expected O, but got Unknown //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016a: 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_0179: Expected O, but got Unknown //IL_017e: Expected O, but got Unknown //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Expected O, but got Unknown //IL_01d5: Expected O, but got Unknown //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_0204: 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_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Expected O, but got Unknown //IL_022c: Expected O, but got Unknown //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Expected O, but got Unknown //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_0320: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_033a: Unknown result type (might be due to invalid IL or missing references) //IL_0341: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_0350: Unknown result type (might be due to invalid IL or missing references) //IL_0356: Unknown result type (might be due to invalid IL or missing references) //IL_0360: Expected O, but got Unknown //IL_0365: Expected O, but got Unknown //IL_0386: Unknown result type (might be due to invalid IL or missing references) //IL_03b1: Unknown result type (might be due to invalid IL or missing references) //IL_03c2: Unknown result type (might be due to invalid IL or missing references) //IL_03cc: Expected O, but got Unknown //IL_03ed: Unknown result type (might be due to invalid IL or missing references) //IL_0418: Unknown result type (might be due to invalid IL or missing references) if (!_stylesReady) { _stylesReady = true; _txDark = MakeTex(new Color32((byte)28, (byte)28, (byte)32, byte.MaxValue)); _txPanel = MakeTex(new Color32((byte)38, (byte)38, (byte)44, byte.MaxValue)); _txSel = MakeTex(new Color32((byte)48, (byte)96, (byte)200, (byte)220)); _txBtn = MakeTex(new Color32((byte)52, (byte)52, (byte)60, byte.MaxValue)); _txBtnHov = MakeTex(new Color32((byte)68, (byte)68, (byte)80, byte.MaxValue)); _winStyle = new GUIStyle(GUI.skin.window) { padding = new RectOffset(12, 12, 12, 12) }; _winStyle.normal.background = _txDark; _winStyle.normal.textColor = Color.white; _winStyle.onNormal.background = _txDark; _header = new GUIStyle(GUI.skin.label) { fontSize = 13, richText = true, padding = new RectOffset(4, 4, 2, 2) }; _header.normal.textColor = Color.white; _subLabel = new GUIStyle(GUI.skin.label) { fontSize = 11, richText = true, padding = new RectOffset(4, 4, 2, 2) }; _subLabel.normal.textColor = new Color(0.8f, 0.8f, 0.8f); _fieldVal = new GUIStyle(GUI.skin.label) { fontSize = 11, richText = false, padding = new RectOffset(4, 4, 2, 2) }; _fieldVal.normal.textColor = new Color(0.55f, 0.55f, 0.55f); _tab = new GUIStyle(GUI.skin.button) { fontSize = 11, richText = false, padding = new RectOffset(8, 8, 4, 4) }; _tab.normal.background = _txBtn; _tab.normal.textColor = new Color(0.72f, 0.72f, 0.72f); _tab.hover.background = _txBtnHov; _tab.hover.textColor = Color.white; _tab.active.background = _txSel; _tab.active.textColor = Color.white; _tabActive = new GUIStyle(_tab) { fontStyle = (FontStyle)1 }; _tabActive.normal.background = _txSel; _tabActive.normal.textColor = Color.white; _tabActive.hover.background = _txSel; _tabActive.hover.textColor = Color.white; _row = new GUIStyle(GUI.skin.button) { alignment = (TextAnchor)3, richText = true, fontSize = 12, padding = new RectOffset(10, 6, 6, 6) }; _row.normal.background = _txPanel; _row.normal.textColor = Color.white; _row.hover.background = _txBtn; _row.hover.textColor = Color.white; _rowSel = new GUIStyle(_row); _rowSel.normal.background = _txSel; _rowSel.normal.textColor = Color.white; _rowSel.hover.background = _txSel; _rowSel.hover.textColor = Color.white; } } private static Texture2D MakeTex(Color32 c) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, Color32.op_Implicit(c)); val.Apply(); return val; } } public static class SpellRegistry { public static List All { get; private set; } = new List(); public static void Refresh() { SpellData[] array = Resources.LoadAll(""); All = (from s in array.Concat(Resources.FindObjectsOfTypeAll()).Distinct() orderby s.voiceCommandKey select s).ToList(); Plugin.Log.LogInfo((object)$"SpellRegistry: found {All.Count} spells ({array.Length} from Resources)."); } public static SpellData Clone(SpellData source, string newName) { string newName2 = newName; SpellData val = ScriptableObject.CreateInstance(); FieldInfo[] fields = typeof(SpellData).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { fieldInfo.SetValue(val, fieldInfo.GetValue(source)); } val.spellName = newName2; if (!All.Any((SpellData s) => s.spellName == newName2)) { All.Add(val); } Plugin.Log.LogInfo((object)("Cloned '" + source.spellName + "' → '" + newName2 + "'")); return val; } public static SpellData? Find(string spellName) { string spellName2 = spellName; return ((IEnumerable)All).FirstOrDefault((Func)((SpellData s) => s.spellName == spellName2)); } } public static class SpellVariants { public static void RegisterTriggers() { } } public static class VoiceInjector { [HarmonyPatch(typeof(VoiceManager), "LoadLocalisationData")] public static class InjectOnLoad { public static void Postfix(VoiceManager __instance) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair> @override in Overrides) { foreach (KeyValuePair item in @override.Value) { if (TryGetTranslatorCommands(__instance, item.Key, out Dictionary commands)) { commands[@override.Key] = item.Value; } } } Plugin.Log.LogInfo((object)$"VoiceInjector: injected {Overrides.Count} overrides."); } } private static readonly Dictionary> Overrides = new Dictionary>(); private static VoiceManager? _vm; private static bool _rebuildLogged; public static IReadOnlyDictionary> All => Overrides; private static VoiceManager? GetVM() { if ((Object)(object)_vm == (Object)null) { _vm = Object.FindAnyObjectByType(); } return _vm; } public static void SetOverride(string locKey, SystemLanguage lang, string[] syllables) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) if (!Overrides.TryGetValue(locKey, out Dictionary value)) { value = new Dictionary(); Overrides[locKey] = value; } value[lang] = syllables.Select((string w) => w.ToUpperInvariant()).ToArray(); PushLiveUpdate(locKey, lang, value[lang]); } public static void RemoveOverride(string locKey) { Overrides.Remove(locKey); } public static bool TryGetOverride(string locKey, SystemLanguage lang, out string[] syllables) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) syllables = Array.Empty(); if (Overrides.TryGetValue(locKey, out Dictionary value)) { return value.TryGetValue(lang, out syllables); } return false; } public static List<(string key, string trigger)> GetAllKeysWithTriggers(SystemLanguage lang = 10) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) VoiceManager vM = GetVM(); if ((Object)(object)vM != (Object)null && TryGetTranslatorCommands(vM, lang, out Dictionary commands)) { return (from k in commands orderby k.Key select (k.Key, string.Join("-", k.Value))).ToList(); } return LoadKeysFromFile(lang); } private static List<(string key, string trigger)> LoadKeysFromFile(SystemLanguage lang) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) try { string path = Path.Combine(Application.streamingAssetsPath, "Vosk", "Localisation"); string path2 = Path.Combine(path, LanguageFileName(lang)); if (!File.Exists(path2)) { path2 = Path.Combine(path, "english.txt"); } List<(string, string)> list = new List<(string, string)>(); string[] array = File.ReadAllLines(path2); foreach (string text in array) { int num = text.IndexOf(" :: ", StringComparison.Ordinal); if (num >= 0) { string text2 = text.Substring(0, num).Trim(); string item = text.Substring(num + 4).Trim(); if (!string.IsNullOrEmpty(text2)) { list.Add((text2, item)); } } } return list.OrderBy(((string, string) x) => x.Item1).ToList(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("VoiceInjector: could not read localization file — " + ex.Message)); return new List<(string, string)>(); } } private static string LanguageFileName(SystemLanguage lang) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Invalid comparison between Unknown and I4 //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Invalid comparison between Unknown and I4 //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Invalid comparison between Unknown and I4 //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Invalid comparison between Unknown and I4 //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Invalid comparison between Unknown and I4 //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Invalid comparison between Unknown and I4 //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Invalid comparison between Unknown and I4 //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Invalid comparison between Unknown and I4 //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Invalid comparison between Unknown and I4 if ((int)lang == 14) { return "french.txt"; } if ((int)lang == 15) { return "german.txt"; } if ((int)lang == 34) { return "spanish.txt"; } if ((int)lang == 21) { return "italian.txt"; } if ((int)lang == 28) { return "portuguese.txt"; } if ((int)lang == 30) { return "russian.txt"; } if ((int)lang == 22) { return "japanese.txt"; } if ((int)lang == 23) { return "korean.txt"; } if ((int)lang == 40) { return "schinese.txt"; } if ((int)lang == 27) { return "polish.txt"; } if ((int)lang == 37) { return "turkish.txt"; } if ((int)lang == 38) { return "ukrainian.txt"; } return "english.txt"; } public static string[]? GetCurrentWords(string locKey, SystemLanguage lang) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) string locKey2 = locKey; VoiceManager vM = GetVM(); if ((Object)(object)vM != (Object)null && TryGetTranslatorCommands(vM, lang, out Dictionary commands)) { if (!commands.TryGetValue(locKey2, out var value)) { return null; } return value; } (string, string) tuple = LoadKeysFromFile(lang).FirstOrDefault<(string, string)>(((string key, string trigger) e) => e.key == locKey2); if (tuple.Item1 == null) { return null; } return ParseInput(tuple.Item2); } public static string[] ParseInput(string input) { return input.ToUpperInvariant().Split(new char[2] { ' ', '-' }, StringSplitOptions.RemoveEmptyEntries); } public static string SyllablesToDisplay(string[]? syllables) { if (syllables == null || syllables.Length == 0) { return ""; } return string.Join("-", syllables); } private static void PushLiveUpdate(string locKey, SystemLanguage lang, string[] syllables) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) VoiceManager vM = GetVM(); if (!((Object)(object)vM == (Object)null) && TryGetTranslatorCommands(vM, lang, out Dictionary commands)) { commands[locKey] = syllables; TryRebuildGrammar(vM); } } private static bool TryGetTranslatorCommands(VoiceManager vm, SystemLanguage lang, out Dictionary commands) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) commands = null; if (!(typeof(VoiceManager).GetField("_voskTranslatorLookup", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(vm) is IDictionary dictionary)) { return false; } object obj = dictionary[lang]; if (obj == null) { return false; } commands = (obj.GetType().GetField("_commands", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(obj) as Dictionary) ?? null; return commands != null; } private static void TryRebuildGrammar(VoiceManager vm) { string[] array = new string[8] { "RebuildGrammar", "RecreateGrammar", "BuildGrammar", "ReloadGrammar", "Reinitialize", "InitGrammar", "SetupGrammar", "LoadGrammar" }; foreach (string name in array) { MethodInfo method = typeof(VoiceManager).GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { method.Invoke(vm, null); _rebuildLogged = false; return; } } if (!_rebuildLogged) { _rebuildLogged = true; Plugin.Log.LogWarning((object)"VoiceInjector: grammar rebuild method not found — override applies on next scene load."); MethodInfo[] methods = typeof(VoiceManager).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); Plugin.Log.LogInfo((object)("VoiceManager methods: " + string.Join(", ", Array.ConvertAll(methods, (MethodInfo m) => m.Name)))); } } } public static class WandRegistry { private static readonly FieldInfo SpellsField = typeof(VoiceSpell).GetField("spells", BindingFlags.Instance | BindingFlags.NonPublic); public static List All { get; private set; } = new List(); public static void Refresh() { All = (from w in Object.FindObjectsByType((FindObjectsSortMode)0) where ((Component)w).gameObject.activeInHierarchy group w by ((NetworkBehaviour)w).netId into g select g.First() into w where (Object)(object)GetVoiceSpell(w) != (Object)null orderby ((Object)((Component)w).gameObject).name select w).ToList(); Plugin.Log.LogInfo((object)$"WandRegistry: found {All.Count} VoiceSpell wands."); } public static VoiceSpell? GetVoiceSpell(NetworkPuppetWandProp wand) { Spell spell = wand.Spell; return (VoiceSpell?)(((object)((spell is VoiceSpell) ? spell : null)) ?? ((object)((Component)wand).GetComponent())); } public static List GetSpellComponents(NetworkPuppetWandProp wand) { VoiceSpell voiceSpell = GetVoiceSpell(wand); if ((Object)(object)voiceSpell == (Object)null) { return new List(); } if (!(SpellsField?.GetValue(voiceSpell) is Spell[] source)) { return new List(); } return source.Where((Spell s) => (Object)(object)s != (Object)null).ToList(); } public static List GetSpells(NetworkPuppetWandProp wand) { return (from s in GetSpellComponents(wand) where (Object)(object)s.SpellData != (Object)null select s.SpellData).ToList(); } public static string GetWandId(NetworkPuppetWandProp wand) { return ((NetworkBehaviour)wand).netId.ToString(); } } }