Decompiled source of SpellEditor v1.1.0
BepInEx/plugins/SpellEditor/SpellEditor.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; 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 Newtonsoft.Json; using Newtonsoft.Json.Converters; using SpellEditor.SpellForge; 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+bc4b4649f47d4453cf14ea973d606f6812266266")] [assembly: AssemblyProduct("SpellEditor")] [assembly: AssemblyTitle("SpellEditor")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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] [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 { public class DummySpawner : MonoBehaviour { private readonly List<GameObject> _dummies = new List<GameObject>(); private void Update() { //IL_009b: 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_0079: 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_008e: 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) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) if (!Input.GetKeyDown((KeyCode)290)) { return; } if (_dummies.Count > 0) { DespawnAll(); return; } if (!NetworkServer.active) { Plugin.Log.LogWarning((object)"[DummySpawner] Must be the server host to spawn dummies."); return; } GameObject val = FindAIPawnPrefab(); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"[DummySpawner] No PawnAIFollower prefab found in registered network prefabs."); return; } NetworkIdentity localPlayer = NetworkClient.localPlayer; Pawn val2 = ((localPlayer != null) ? ((Component)localPlayer).GetComponent<Pawn>() : null); Vector3 val3 = ((!((Object)(object)val2 != (Object)null)) ? (Vector3.forward * 4f) : (((Component)val2).transform.position + ((Component)val2).transform.forward * 4f)); GameObject val4 = Object.Instantiate<GameObject>(val, val3, Quaternion.identity); NetworkServer.Spawn(val4, (NetworkConnectionToClient)null); PawnAIFollower component = val4.GetComponent<PawnAIFollower>(); if ((Object)(object)component != (Object)null) { component.ExternalControlEnabled = true; AIInputProvider component2 = val4.GetComponent<AIInputProvider>(); if ((Object)(object)component2 != (Object)null) { component2.UsePathfinding = false; component2.StoppingDistance = 0.1f; } } _dummies.Add(val4); Plugin.Log.LogInfo((object)$"[DummySpawner] Spawned AI dummy at {val3}. Press F9 to despawn."); } private static GameObject? FindAIPawnPrefab() { foreach (GameObject spawnPrefab in NetworkManager.singleton.spawnPrefabs) { if ((Object)(object)spawnPrefab != (Object)null && (Object)(object)spawnPrefab.GetComponent<PawnAIFollower>() != (Object)null) { return spawnPrefab; } } return null; } private void DespawnAll() { foreach (GameObject dummy in _dummies) { if (!((Object)(object)dummy == (Object)null)) { NetworkServer.UnSpawn(dummy); Object.Destroy((Object)(object)dummy); } } Plugin.Log.LogInfo((object)$"[DummySpawner] Despawned {_dummies.Count} dummy(s)."); _dummies.Clear(); } private void OnDestroy() { DespawnAll(); } } [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<Pawn>(); if (!((Object)(object)component == (Object)null) && ((NetworkBehaviour)component).isLocalPlayer && !((Object)(object)component.Voice == (Object)null)) { PawnVoice voice = component.Voice; voice.OnVoiceResult = (Action<string>)Delegate.Combine(voice.OnVoiceResult, new Action<string>(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_005e: 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) 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) { return; } _activationTimes.TryGetValue(((Object)val).GetInstanceID(), out var value); if (!(Time.time - value < 3f) && !(rigidbody.linearVelocity.y > 0.5f) && Physics.Raycast(((Component)val).transform.position, Vector3.down, 2f)) { StatusFlying lastStatus = val.PawnStatus.GetLastStatus<StatusFlying>(); 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 readonly Dictionary<int, float> _activationTimes = new Dictionary<int, float>(); 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<FlightRequestMessage>((Action<NetworkConnectionToClient, FlightRequestMessage>)OnServerReceive, false); } catch { } try { NetworkClient.RegisterHandler<FlightRequestMessage>((Action<FlightRequestMessage>)OnClientReceive, false); } catch { } } private static void OnVoiceResult(string input) { if (string.IsNullOrEmpty(input) || !MatchesTrigger(input)) { return; } if (NetworkServer.active) { Pawn val = ((IEnumerable<Pawn>)Object.FindObjectsByType<Pawn>((FindObjectsSortMode)0)).FirstOrDefault((Func<Pawn, bool>)((Pawn p) => ((NetworkBehaviour)p).isLocalPlayer)); if ((Object)(object)val != (Object)null) { Apply(val); } } else if (NetworkClient.isConnected) { NetworkClient.Send<FlightRequestMessage>(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<Pawn>() : null); if ((Object)(object)val != (Object)null) { Apply(val); } } private static void OnClientReceive(FlightRequestMessage msg) { } private static void Apply(Pawn pawn) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) if (!NetworkServer.active) { return; } StatusFlyingSO statusFlyingSO = GetStatusFlyingSO(); if ((Object)(object)statusFlyingSO == (Object)null) { Plugin.Log.LogWarning((object)"[FlightCommand] StatusFlyingSO not found in resources."); return; } PawnInventory component = ((Component)pawn).GetComponent<PawnInventory>(); if (component != null) { component.SvDropAllItemsFromInventory(); } PawnPropInteractions component2 = ((Component)pawn).GetComponent<PawnPropInteractions>(); if ((Object)(object)component2 != (Object)null) { NetworkPuppetProp networkCurrentRightHandNetworkProp = component2.NetworkCurrentRightHandNetworkProp; NetworkPuppetProp networkCurrentLeftHandNetworkProp = component2.NetworkCurrentLeftHandNetworkProp; if (networkCurrentRightHandNetworkProp != null) { networkCurrentRightHandNetworkProp.ServerHandleDrop(true, default(Vector3)); } if ((Object)(object)networkCurrentLeftHandNetworkProp != (Object)(object)networkCurrentRightHandNetworkProp && networkCurrentLeftHandNetworkProp != null) { networkCurrentLeftHandNetworkProp.ServerHandleDrop(true, default(Vector3)); } } _activationTimes[((Object)pawn).GetInstanceID()] = 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<StatusFlyingSO>().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<WandSpellFieldMessage>((Action<NetworkConnectionToClient, WandSpellFieldMessage>)OnServerReceive, false); } catch { } try { NetworkClient.RegisterHandler<WandSpellFieldMessage>((Action<WandSpellFieldMessage>)OnClientReceive, false); } catch { } } public void BroadcastFieldChange(NetworkPuppetWandProp wand, string fieldName, string value) { WandSpellFieldMessage wandSpellFieldMessage = new WandSpellFieldMessage { WandNetId = WandRegistry.GetWandId(wand), FieldName = fieldName, FieldValue = value }; if (NetworkServer.active) { NetworkServer.SendToAll<WandSpellFieldMessage>(wandSpellFieldMessage, 0, false); } else if (NetworkClient.isConnected) { NetworkClient.Send<WandSpellFieldMessage>(wandSpellFieldMessage, 0); } else { ApplyFieldChange(wandSpellFieldMessage); } } private static void ApplyFieldChange(WandSpellFieldMessage msg) { NetworkPuppetWandProp val = ((IEnumerable<NetworkPuppetWandProp>)WandRegistry.All).FirstOrDefault((Func<NetworkPuppetWandProp, bool>)((NetworkPuppetWandProp w) => WandRegistry.GetWandId(w) == msg.WandNetId)); if ((Object)(object)val == (Object)null) { WandRegistry.Refresh(); val = ((IEnumerable<NetworkPuppetWandProp>)WandRegistry.All).FirstOrDefault((Func<NetworkPuppetWandProp, bool>)((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<WandSpellFieldMessage>(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<Pawn>(); 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<ProjectileTornado> _oculusTornadoes = new HashSet<ProjectileTornado>(); public static void RegisterProjectilePrefab(GameObject prefab) { } } [BepInPlugin("com.local.yapyap.SpellEditor", "SpellEditor", "1.1.0")] public class Plugin : BaseUnityPlugin { public const string GUID = "com.local.yapyap.SpellEditor"; public const string Name = "SpellEditor"; public const string Version = "1.1.0"; private static readonly Dictionary<string, string> RequiredVoiceEntries = new Dictionary<string, string> { { "SPELL_BAS_FEATHER_FALL", "FEATHER-FALL" }, { "SPELL_BAS_DOWN_DOG", "DOWN-DOG" }, { "SPELL_BAS_AERO_MAXIMA", "FUS" }, { "SPELL_BAS_OCULUS_TEMPEST", "OCULUS-TEMPEST" }, { "SPELL_BAS_UP_YOURS", "UP-YOURS" }, { "PLAYER_COMMAND_WITNESS_ME", "WITNESS-ME" }, { "PLAYER_COMMAND_BECOME_WIND", "LET-GO-YOUR-EARTHLY-TETHER-ENTER-THE-VOID-EMPTY-AND-BECOME-WIND" } }; internal static ManualLogSource Log { get; private set; } = null; internal static Plugin Instance { 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<string> list = new List<string>(File.ReadAllLines(text)); bool changed = false; foreach (KeyValuePair<string, string> requiredVoiceEntry in RequiredVoiceEntries) { string key = requiredVoiceEntry.Key; string value = requiredVoiceEntry.Value; string text2 = key + " :: " + value; bool flag = false; foreach (string item in list) { if (item.StartsWith(key + " ::")) { flag = true; break; } } if (!flag) { list.Add(text2); changed = true; Log.LogInfo((object)("[SpellEditor] Added voice entry: " + text2)); } } SpellInterpreter.PatchEnglishTxt(list, ref changed); if (changed) { File.WriteAllLines(text, list); } else { Log.LogInfo((object)"[SpellEditor] english.txt already up to date."); } } private void Awake() { //IL_0039: 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_010b: Expected O, but got Unknown //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Instance = this; SpellInterpreter.Initialize(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "variants.json")); PatchEnglishTxt(); new Harmony("com.local.yapyap.SpellEditor").PatchAll(Assembly.GetExecutingAssembly()); FlightCommand.RegisterHandlers(); WitnessCommand.RegisterHandlers(); SpellVariants.RegisterTriggers(); SpellInterpreter.RegisterHandlers(); PrefabInjector.RegisterHandlers(); 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_DOWN_DOG", (SystemLanguage)10, VoiceInjector.ParseInput("DOWN-DOG")); VoiceInjector.SetOverride("SPELL_BAS_AERO_MAXIMA", (SystemLanguage)10, VoiceInjector.ParseInput("FUS")); VoiceInjector.SetOverride("SPELL_BAS_UP_YOURS", (SystemLanguage)10, VoiceInjector.ParseInput("UP-YOURS")); VoiceInjector.SetOverride("PLAYER_COMMAND_WITNESS_ME", (SystemLanguage)10, VoiceInjector.ParseInput("WITNESS-ME")); GameObject val = new GameObject("SpellEditorRoot"); Object.DontDestroyOnLoad((Object)val); val.AddComponent<SpellEditorWindow>(); val.AddComponent<NetworkSync>(); val.AddComponent<DummySpawner>(); Log.LogInfo((object)"SpellEditor 1.1.0 loaded. Press F8 to open."); } } public static class PrefabInjector { [HarmonyPatch(typeof(UpdraftSpell), "Update")] public static class FeatherFallLandingPatch { public static void Postfix(UpdraftSpell __instance) { //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: 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) if (!IsFeatherVariant(((Spell)__instance).SpellData?.voiceCommandKey) || !(_isUpdraftActiveField?.GetValue(__instance) is int num) || num == 0) { return; } object? obj = _casterPawnField?.GetValue(__instance); Pawn val = (Pawn)((obj is Pawn) ? obj : null); if ((Object)(object)val == (Object)null || !((NetworkBehaviour)val).isLocalPlayer) { return; } float num2 = ((_updraftStartTimeField?.GetValue(__instance) is float num3) ? num3 : 0f); if (!(Time.time - num2 < 3f)) { object? obj2 = _getActiveRigidbodyMethod?.Invoke(__instance, null); Rigidbody val2 = (Rigidbody)((obj2 is Rigidbody) ? obj2 : 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<Spell> allSpells)) { return; } TryAdd(__instance, allSpells, (Spell?)(object)((IEnumerable<UpdraftSpell>)((Component)__instance).GetComponents<UpdraftSpell>()).FirstOrDefault((Func<UpdraftSpell, bool>)((UpdraftSpell u) => ((Spell)u).SpellData?.voiceCommandKey == "SPELL_BAS_FEATHER_FALL"))); TryAdd(__instance, allSpells, (Spell?)(object)((IEnumerable<UpdraftSpell>)((Component)__instance).GetComponents<UpdraftSpell>()).FirstOrDefault((Func<UpdraftSpell, bool>)((UpdraftSpell u) => ((Spell)u).SpellData?.voiceCommandKey == "SPELL_BAS_DOWN_DOG"))); TryAdd(__instance, allSpells, ((IEnumerable<Spell>)((Component)__instance).GetComponents<Spell>()).FirstOrDefault((Func<Spell, bool>)((Spell s) => ((s == null) ? null : s.SpellData?.voiceCommandKey) == "SPELL_BAS_AERO_MAXIMA"))); TryAdd(__instance, allSpells, ((IEnumerable<Spell>)((Component)__instance).GetComponents<Spell>()).FirstOrDefault((Func<Spell, bool>)((Spell s) => ((s == null) ? null : s.SpellData?.voiceCommandKey) == "SPELL_BAS_OCULUS_TEMPEST"))); TryAdd(__instance, allSpells, (Spell?)(object)((IEnumerable<UpdraftSpell>)((Component)__instance).GetComponents<UpdraftSpell>()).FirstOrDefault((Func<UpdraftSpell, bool>)((UpdraftSpell u) => ((Spell)u).SpellData?.voiceCommandKey == "SPELL_BAS_UP_YOURS"))); foreach (SpellVariantDef variant in SpellInterpreter.GetAllVariants()) { TryAdd(__instance, allSpells, ((IEnumerable<Spell>)((Component)__instance).GetComponents<Spell>()).FirstOrDefault((Func<Spell, bool>)((Spell s) => ((s == null) ? null : s.SpellData?.voiceCommandKey) == variant.LocKey))); } } private static void TryAdd(VoiceSpell vs, List<Spell> 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 (!IsFeatherVariant(__instance.SpellData?.voiceCommandKey)) { 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 (IsFeatherVariant(((Spell)__instance).SpellData?.voiceCommandKey) && 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 (IsFeatherVariant(__instance.SpellData?.voiceCommandKey) && NetworkServer.active) { UpdraftSpell? obj = FindParentUpDog((Component)(object)__instance); if (obj != null) { ((Spell)obj).OnSpellDeactivate(); } } } } [HarmonyPatch(typeof(Spell), "CanCast")] public static class UpYoursCanCastPatch { public static bool Prefix(Spell __instance, ref bool __result) { if (__instance.SpellData?.voiceCommandKey != "SPELL_BAS_UP_YOURS") { 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 UpYoursActivatePatch { public static bool Prefix(UpdraftSpell __instance) { if (((Spell)__instance).SpellData?.voiceCommandKey != "SPELL_BAS_UP_YOURS") { return true; } if (!NetworkServer.active) { return false; } object? obj = _casterPawnField?.GetValue(__instance); Pawn val = (Pawn)((obj is Pawn) ? obj : null); if ((Object)(object)val == (Object)null) { return false; } Pawn val2 = FindUpYoursTarget(val); if ((Object)(object)val2 == (Object)null) { return false; } UpdraftSpell? obj2 = FindParentUpDog((Component)(object)__instance); if (obj2 != null) { ((Spell)obj2).SetCastData(); } ApplyUpdraftToTarget(val2); return false; } } [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)) { _lastYellTime = Time.time; object? obj = _casterPawnField?.GetValue(__instance); Pawn val2 = (Pawn)((obj is Pawn) ? obj : null); if (!((Object)(object)val2 == (Object)null)) { object obj2 = _pawnEmotesEmoteStateField?.GetValue(val2.Emotes); _emotePlayMethod?.Invoke(obj2, new object[2] { _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 static readonly MethodInfo _targetRpcActivateMethod = typeof(UpdraftSpell).GetMethod("TargetRpcSpellActivate", 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 readonly FieldInfo? _pawnEmotesEmoteStateField = typeof(PawnEmotes).GetField("emoteState", BF); private static readonly MethodInfo? _emotePlayMethod = typeof(ControllerStateEmote).GetMethod("PlayEmote", BF, null, new Type[2] { typeof(int), typeof(float) }, null); private static bool IsFeatherVariant(string? key) { if (!(key == "SPELL_BAS_FEATHER_FALL")) { return key == "SPELL_BAS_DOWN_DOG"; } return true; } private static UpdraftSpell? FindParentUpDog(Component feather) { return Array.Find(feather.GetComponents<UpdraftSpell>(), (UpdraftSpell u) => ((Spell)u).SpellData?.voiceCommandKey == "SPELL_BAS_UPDOG"); } private static Pawn? FindUpYoursTarget(Pawn caster) { //IL_0001: 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_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0063: 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) Vector3 lookOrigin = caster.LookOrigin; Vector3 aimDirection = caster.AimDirection; float num = 20f; RaycastHit val = default(RaycastHit); if (Physics.Raycast(lookOrigin, aimDirection, ref val, num)) { num = ((RaycastHit)(ref val)).distance; } RaycastHit[] array = Physics.SphereCastAll(lookOrigin, 0.5f, aimDirection, num, -1, (QueryTriggerInteraction)1); Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance)); RaycastHit[] array2 = array; Pawn val4 = default(Pawn); for (int num2 = 0; num2 < array2.Length; num2++) { RaycastHit val2 = array2[num2]; if (!((Component)((RaycastHit)(ref val2)).collider).CompareTag("IgnoreCollision") && !((Component)((RaycastHit)(ref val2)).collider).transform.IsChildOf(((Component)caster).transform)) { NetworkIdentity val3 = ((Component)((RaycastHit)(ref val2)).collider).GetComponent<NetworkIdentity>() ?? ((Component)((RaycastHit)(ref val2)).collider).GetComponentInParent<NetworkIdentity>(); if (!((Object)(object)val3 == (Object)null) && ((Component)val3).TryGetComponent<Pawn>(ref val4) && !((Object)(object)val4 == (Object)(object)caster)) { return val4; } } } return null; } private static void ApplyUpdraftToTarget(Pawn target) { UpdraftSpell val = FindUpdraftOnPawn(target, "SPELL_BAS_UPDOG"); if ((Object)(object)val != (Object)null && ((NetworkBehaviour)target).connectionToClient != null) { val.NetworksyncedUpdraftActive = true; _targetRpcActivateMethod?.Invoke(val, new object[1] { ((NetworkBehaviour)target).connectionToClient }); return; } PawnControllerStateMachine controllerStateMachine = target.ControllerStateMachine; Rigidbody val2 = ((controllerStateMachine != null) ? controllerStateMachine.Rigidbody : null); if ((Object)(object)val2 != (Object)null && !val2.isKinematic) { ((Component)target).gameObject.AddComponent<UpdraftApplicator>().Initialize(val2); } } private static UpdraftSpell? FindUpdraftOnPawn(Pawn pawn, string spellKey) { NetworkPuppetProp networkCurrentRightHandNetworkProp = pawn.PropInteractions.NetworkCurrentRightHandNetworkProp; NetworkPuppetProp networkCurrentLeftHandNetworkProp = pawn.PropInteractions.NetworkCurrentLeftHandNetworkProp; return FindByKey(networkCurrentRightHandNetworkProp, spellKey) ?? FindByKey(networkCurrentLeftHandNetworkProp, spellKey); static UpdraftSpell? FindByKey(NetworkPuppetProp? prop, string key) { if (!((Object)(object)prop == (Object)null)) { return Array.Find(((Component)prop).GetComponents<UpdraftSpell>(), (UpdraftSpell u) => ((u == null) ? null : ((Spell)u).SpellData?.voiceCommandKey) == key); } return null; } } private static void InjectUpYours(NetworkPuppetWandProp wand) { if (!Array.Exists(((Component)wand).GetComponents<Spell>(), (Spell s) => ((s == null) ? null : s.SpellData?.voiceCommandKey) == "SPELL_BAS_UP_YOURS")) { UpdraftSpell val = Array.Find(((Component)wand).GetComponents<UpdraftSpell>(), (UpdraftSpell u) => ((u == null) ? null : ((Spell)u).SpellData?.voiceCommandKey) == "SPELL_BAS_UPDOG"); if ((Object)(object)((val != null) ? ((Spell)val).SpellData : null) == (Object)null) { Plugin.Log.LogWarning((object)"[PrefabInjector] Up-Yours: source UPDOG spell missing."); return; } SpellData val2 = SpellRegistry.Clone(((Spell)val).SpellData, "UP-YOURS"); SetSdField(val2, "voiceCommandKey", "SPELL_BAS_UP_YOURS"); UpdraftSpell val3 = ((Component)wand).gameObject.AddComponent<UpdraftSpell>(); CopySerializedFields(typeof(UpdraftSpell), val, val3); _spellDataField?.SetValue(val3, val2); _isSecretField?.SetValue(val3, true); ((Behaviour)val3).enabled = true; Plugin.Log.LogInfo((object)"[PrefabInjector] Up-Yours added."); } } public static void RegisterHandlers() { } private static Spell? FindParentAero(Component variant) { return Array.Find(variant.GetComponents<Spell>(), (Spell s) => ((s == null) ? null : s.SpellData?.voiceCommandKey) == "SPELL_BAS_VENTO"); } private static Spell? FindParentTempest(Component variant) { return Array.Find(variant.GetComponents<Spell>(), (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); InjectDownDog(val); InjectAeroMaxima(val); InjectOculusTempest(val); InjectUpYours(val); InjectDataDrivenVariants(val); _done = true; } } private static void InjectFeatherFall(NetworkPuppetWandProp wand) { UpdraftSpell[] components = ((Component)wand).GetComponents<UpdraftSpell>(); 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<UpdraftSpell>(); 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 InjectDownDog(NetworkPuppetWandProp wand) { if (!Array.Exists(((Component)wand).GetComponents<Spell>(), (Spell s) => ((s == null) ? null : s.SpellData?.voiceCommandKey) == "SPELL_BAS_DOWN_DOG")) { UpdraftSpell val = Array.Find(((Component)wand).GetComponents<UpdraftSpell>(), (UpdraftSpell u) => ((u == null) ? null : ((Spell)u).SpellData?.voiceCommandKey) == "SPELL_BAS_FEATHER_FALL"); if ((Object)(object)((val != null) ? ((Spell)val).SpellData : null) == (Object)null) { Plugin.Log.LogWarning((object)"[PrefabInjector] Down-Dog: source Feather-Fall spell missing."); return; } SpellData val2 = SpellRegistry.Clone(((Spell)val).SpellData, "DOWN-DOG"); SetSdField(val2, "voiceCommandKey", "SPELL_BAS_DOWN_DOG"); UpdraftSpell val3 = ((Component)wand).gameObject.AddComponent<UpdraftSpell>(); CopySerializedFields(typeof(UpdraftSpell), val, val3); _spellDataField?.SetValue(val3, val2); _isSecretField?.SetValue(val3, true); ((Behaviour)val3).enabled = true; Plugin.Log.LogInfo((object)"[PrefabInjector] Down-Dog added."); } } private static void InjectOculusTempest(NetworkPuppetWandProp wand) { Spell[] components = ((Component)wand).GetComponents<Spell>(); 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<GameObject>(original, val.transform); OculusTempestVariant.RegisterProjectilePrefab(obj); return obj; } private static void InjectAeroMaxima(NetworkPuppetWandProp wand) { Spell[] components = ((Component)wand).GetComponents<Spell>(); 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_004f: 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<GameObject>(original, val.transform); obj.transform.localScale = original.transform.localScale * Mathf.Pow(2f, 1f / 3f); ProjectilePush component = obj.GetComponent<ProjectilePush>(); if ((Object)(object)component != (Object)null) { float num = ((typeof(ProjectilePush).GetField("pushForce", BF)?.GetValue(component) is float num2) ? num2 : 10f); SetCompField((Component)(object)component, "pushForce", num * 2f); float num3 = ((BaseProjectile)component).ProjectileData?.maxTravelDistance ?? 30f; SetCompField((Component)(object)component, "maxTravelDistanceOverride", num3 * Mathf.Pow(2f, 1f / 3f)); return obj; } Plugin.Log.LogWarning((object)"[PrefabInjector] Aero-Maxima: ProjectilePush not found on clone."); return obj; } private static void InjectDataDrivenVariants(NetworkPuppetWandProp wand) { //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Expected O, but got Unknown Spell[] components = ((Component)wand).GetComponents<Spell>(); foreach (SpellVariantDef variant in SpellInterpreter.GetAllVariants()) { if (!Array.Exists(components, (Spell s) => ((s == null) ? null : s.SpellData?.voiceCommandKey) == variant.LocKey)) { Spell val = Array.Find(components, (Spell s) => ((s == null) ? null : s.SpellData?.voiceCommandKey) == variant.BaseSpellKey); if ((Object)(object)((val != null) ? val.SpellData : null) == (Object)null) { Plugin.Log.LogWarning((object)("[PrefabInjector] Variant '" + variant.LocKey + "': base spell '" + variant.BaseSpellKey + "' not found.")); } else { SpellData val2 = SpellRegistry.Clone(val.SpellData, variant.VoiceCommand); SetSdField(val2, "voiceCommandKey", variant.LocKey); Spell val3 = (Spell)((Component)wand).gameObject.AddComponent(((object)val).GetType()); CopySerializedFields(((object)val).GetType(), val, val3); _spellDataField?.SetValue(val3, val2); _isSecretField?.SetValue(val3, true); ((Behaviour)val3).enabled = true; Plugin.Log.LogInfo((object)("[PrefabInjector] Injected data-driven variant: " + variant.LocKey)); } } } } 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<NetworkPuppetWandProp>(), 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); } private static T GetCompField<T>(Component comp, string name, T fallback) { object obj = ((object)comp).GetType().GetField(name, BF)?.GetValue(comp); if (obj is T) { return (T)obj; } return fallback; } } public class UpdraftApplicator : MonoBehaviour { private Rigidbody _rb; private float _elapsed; private const float InitialForce = 750f; private const float ContinuousForce = 200f; private const float FallSlowFactor = 0.3f; private const float MaxVelocity = 15f; private const float Duration = 5f; public void Initialize(Rigidbody rb) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) _rb = rb; rb.AddForce(Vector3.up * 750f, (ForceMode)1); } private void Update() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: 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_00cf: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_rb == (Object)null) { Object.Destroy((Object)(object)this); return; } _elapsed += Time.deltaTime; if (_elapsed >= 5f) { Object.Destroy((Object)(object)this); return; } Vector3 linearVelocity = _rb.linearVelocity; if (linearVelocity.y < 0f) { linearVelocity.y *= 0.3f; _rb.linearVelocity = linearVelocity; } else if (linearVelocity.y < 15f) { _rb.AddForce(Vector3.up * 200f * Time.deltaTime, (ForceMode)0); } linearVelocity = _rb.linearVelocity; if (linearVelocity.y > 15f) { linearVelocity.y = 15f; _rb.linearVelocity = linearVelocity; } } } public class SpellEditorWindow : MonoBehaviour { private bool _visible; private Rect _windowRect = new Rect(20f, 20f, 1200f, 700f); private bool _selectedIsVariant; private int _selectedNode; private Vector2 _nodeScroll; private string _search = ""; private string _schoolFilter = "ALL"; private Vector2 _listScroll; private int _selectedIndex = -1; private List<SpellData> _allSpells = new List<SpellData>(); private List<SpellData> _filtered = new List<SpellData>(); private Dictionary<string, string> _keyTriggers = new Dictionary<string, string>(); private List<(FieldInfo field, string draft, string live)> _chargeFields = new List<(FieldInfo, string, string)>(); private string _editKey = ""; private string _editTrigger = ""; private List<SpellVariantDef> _variants = new List<SpellVariantDef>(); private int _selVariant = -1; private Vector2 _variantDetailScroll; private bool _wizardActive; private int _wizardStep; private string _newLocKey = "SPELL_CUSTOM_"; private string _newBaseKey = ""; private string _newVoiceCmd = ""; private SpellVariantDef _wizardDef = new SpellVariantDef { Gate = new GateDef(), Targeting = new TargetingDef() }; private string _wizardRadius = "0.50"; private string _wizardMaxDist = "20.0"; private string _wizardError = ""; private string _draftRadius = ""; private string _draftMaxDist = ""; private string _draftMinCharges = ""; private bool _addingEffect; private int _addEffectTypeIdx; private string _addEffectSpellKey = ""; private string[] _variantErrors = Array.Empty<string>(); private static readonly Dictionary<string, Color32> Schools = new Dictionary<string, Color32> { ["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 static readonly HashSet<string> ChargeFieldNames = new HashSet<string> { "cooldown", "charges", "chargePerCast", "chargeCostPerSecondCast", "regenInterval", "regenPerInterval", "regenDelay" }; private static readonly string[] NodeNames = new string[5] { "Voice", "Charges", "Activation", "Targeting", "Effects" }; 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 GUIStyle _nodeBtn; private GUIStyle _nodeBtnSel; private Texture2D _txDark; private Texture2D _txPanel; private Texture2D _txSel; private Texture2D _txBtn; private Texture2D _txBtnHov; private CursorLockMode _savedLockState; private bool _savedCursorVisible; private static readonly string[] WizardStepNames = new string[5] { "Identity", "Gate", "Targeting", "Effects", "Review" }; private void Update() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (Input.GetKeyDown((KeyCode)289)) { _visible = !_visible; if (_visible) { _savedLockState = Cursor.lockState; _savedCursorVisible = Cursor.visible; Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; Refresh(); } else { Cursor.lockState = _savedLockState; Cursor.visible = _savedCursorVisible; } } } 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<GUILayoutOption>()); } } 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); _variants = SpellInterpreter.GetAllVariants().ToList(); HashSet<string> variantKeys = new HashSet<string>(_variants.Select((SpellVariantDef v) => v.LocKey)); _allSpells = SpellRegistry.All.Where((SpellData s) => !string.IsNullOrEmpty(s.voiceCommandKey) && !variantKeys.Contains(s.voiceCommandKey)).ToList(); ApplyFilter(); if (_selectedIndex >= _filtered.Count) { SelectSpell(-1); } else if (_selectedIndex >= 0 && !_selectedIsVariant) { LoadChargeFields(_filtered[_selectedIndex]); } if (_selVariant >= _variants.Count) { SelectVariant(-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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) _selectedIndex = index; _selectedIsVariant = false; _selectedNode = 0; _nodeScroll = Vector2.zero; _selVariant = -1; if (index < 0 || index >= _filtered.Count) { _chargeFields = new List<(FieldInfo, string, string)>(); _editKey = (_editTrigger = ""); return; } SpellData val = _filtered[index]; _editKey = val.voiceCommandKey ?? ""; _editTrigger = (_keyTriggers.TryGetValue(_editKey, out string value) ? value : ""); LoadChargeFields(val); } private void SelectVariant(int i) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) _selVariant = i; if (i >= 0) { _selectedIsVariant = true; _selectedIndex = -1; _selectedNode = 0; } else { _selectedIsVariant = false; } _nodeScroll = Vector2.zero; _variantDetailScroll = Vector2.zero; _variantErrors = Array.Empty<string>(); _addingEffect = false; if (i < 0 || i >= _variants.Count) { _draftRadius = (_draftMaxDist = (_draftMinCharges = "")); return; } SpellVariantDef spellVariantDef = _variants[i]; _draftRadius = spellVariantDef.Targeting?.Radius.ToString("F2") ?? "0.50"; _draftMaxDist = spellVariantDef.Targeting?.MaxDistance.ToString("F1") ?? "20.0"; _draftMinCharges = spellVariantDef.Gate?.MinCharges.ToString() ?? "1"; } private void LoadChargeFields(SpellData sd) { _chargeFields = (from f in typeof(SpellData).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where ChargeFieldNames.Contains(f.Name) && IsEditable(f.FieldType) select f).Select(delegate(FieldInfo f) { string text = f.GetValue(sd)?.ToString() ?? ""; return (f: f, text, text); }).ToList(); } private void ApplyChargeFields(SpellData sd) { foreach (var (fieldInfo, text, _) in _chargeFields) { 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] Charge fields applied to '" + sd.spellName + "'.")); } private void DrawWindow(int id) { //IL_0130: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("<b>SpellForge</b> <size=10><color=#777>v2.0 · F8 to toggle</color></size>", _header, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label($"<size=10><color=#555>{SpellRegistry.All.Count} spells · {WandRegistry.All.Count} wands · {_variants.Count} variants</color></size>", _subLabel, Array.Empty<GUILayoutOption>()); GUILayout.Space(8f); if (GUILayout.Button("Refresh", _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { Refresh(); } GUILayout.EndHorizontal(); DrawSeparator(); GUILayout.Space(4f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) }); DrawSpellList(); GUILayout.EndVertical(); GUILayout.Space(10f); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); DrawPipelineView(); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, 30f)); } private void DrawSpellList() { //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0079: 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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_0202: 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_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) string text = GUILayout.TextField(_search, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (text != _search) { _search = text; ApplyFilter(); } GUILayout.Space(4f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.FlexibleSpace(); string[] schoolKeys = SchoolKeys; foreach (string text2 in schoolKeys) { Color32 value; ? val = ((!(_schoolFilter == text2)) ? new Color32((byte)50, (byte)50, (byte)58, byte.MaxValue) : (Schools.TryGetValue(text2, out value) ? value : new Color32((byte)80, (byte)120, (byte)220, byte.MaxValue))); Color backgroundColor = GUI.backgroundColor; GUI.backgroundColor = Color32.op_Implicit((Color32)val); string text3 = text2; GUIStyle tab = _tab; bool flag = ((text2 == "ALL" || text2 == "ICE") ? true : false); if (GUILayout.Button(text3, tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width((float)(flag ? 34 : 30)) })) { _schoolFilter = text2; ApplyFilter(); } GUI.backgroundColor = backgroundColor; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Space(4f); DrawSeparator(); _listScroll = GUILayout.BeginScrollView(_listScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); GUILayout.Label($"<size=9><color=#666>BASE SPELLS {_filtered.Count}</color></size>", _subLabel, Array.Empty<GUILayoutOption>()); GUILayout.Space(2f); for (int j = 0; j < _filtered.Count; j++) { SpellData val2 = _filtered[j]; string school = GetSchool(val2.voiceCommandKey); string value2; string text4 = (_keyTriggers.TryGetValue(val2.voiceCommandKey ?? "", out value2) ? value2 : "?"); Color32 value3; string text5 = (Schools.TryGetValue(school, out value3) ? $"#{value3.r:X2}{value3.g:X2}{value3.b:X2}" : "#888"); if (GUILayout.Button("<color=" + text5 + ">◆</color> <b>" + val2.spellName + "</b>\n<size=10><color=#888>" + text4 + "</color></size>", (!_selectedIsVariant && j == _selectedIndex) ? _rowSel : _row, Array.Empty<GUILayoutOption>())) { SelectSpell(j); } } GUILayout.Space(8f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label($"<size=9><color=#666>VARIANTS {_variants.Count}</color></size>", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (GUILayout.Button(_wizardActive ? "✕" : "+", _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(28f) })) { if (_wizardActive) { CancelWizard(); } else { StartWizard(); } } GUILayout.EndHorizontal(); GUILayout.Space(2f); for (int k = 0; k < _variants.Count; k++) { SpellVariantDef spellVariantDef = _variants[k]; if (string.IsNullOrEmpty(_search) || spellVariantDef.VoiceCommand.IndexOf(_search, StringComparison.OrdinalIgnoreCase) >= 0 || spellVariantDef.LocKey.IndexOf(_search, StringComparison.OrdinalIgnoreCase) >= 0) { bool flag2 = _selectedIsVariant && k == _selVariant; if (GUILayout.Button("<color=#4080ff>⬡</color> <b>" + spellVariantDef.VoiceCommand + "</b>\n<size=10><color=#888>" + spellVariantDef.LocKey + "</color></size>", flag2 ? _rowSel : _row, Array.Empty<GUILayoutOption>())) { SelectVariant(k); } } } GUILayout.EndScrollView(); } private void DrawPipelineView() { //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_0286: 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_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) if (_wizardActive) { DrawWizard(); return; } bool flag = !_selectedIsVariant && _selectedIndex >= 0 && _selectedIndex < _filtered.Count; bool flag2 = _selectedIsVariant && _selVariant >= 0 && _selVariant < _variants.Count; if (!flag && !flag2) { GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.FlexibleSpace(); GUILayout.Label("<color=#444>← select a spell or variant</color>", _subLabel, Array.Empty<GUILayoutOption>()); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.FlexibleSpace(); return; } if (flag) { 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.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("<b><size=15>" + val.spellName + "</size></b> <color=" + text + "><size=11>" + school + "</size></color> <size=10><color=#555>" + val.voiceCommandKey + "</color></size>", _header, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("<size=10><color=#4a4> BASE </color></size>", _subLabel, Array.Empty<GUILayoutOption>()); GUILayout.EndHorizontal(); } else { SpellVariantDef spellVariantDef = _variants[_selVariant]; GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("<b><size=15>" + spellVariantDef.VoiceCommand + "</size></b> <size=10><color=#555>" + spellVariantDef.LocKey + "</color></size>", _header, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("<size=10><color=#4080ff> VARIANT </color></size>", _subLabel, Array.Empty<GUILayoutOption>()); Color backgroundColor = GUI.backgroundColor; GUI.backgroundColor = new Color(0.55f, 0.18f, 0.18f); if (GUILayout.Button("Delete", _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { SpellInterpreter.DeleteVariant(spellVariantDef.LocKey); _variants = SpellInterpreter.GetAllVariants().ToList(); SelectVariant(-1); GUI.backgroundColor = backgroundColor; return; } GUI.backgroundColor = backgroundColor; GUILayout.EndHorizontal(); } DrawSeparator(); GUILayout.Space(4f); DrawPipelineBar(); GUILayout.Space(4f); DrawSeparator(); GUILayout.Space(4f); _nodeScroll = GUILayout.BeginScrollView(_nodeScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); DrawNodePanel(); GUILayout.EndScrollView(); if (flag2) { DrawSeparator(); GUILayout.Space(4f); DrawVariantFooter(); } } private void DrawPipelineBar() { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.FlexibleSpace(); for (int i = 0; i < NodeNames.Length; i++) { string nodeSummary = GetNodeSummary(i); if (GUILayout.Button("<b>" + NodeNames[i] + "</b>\n<size=9><color=#aaa>" + nodeSummary + "</color></size>", (i == _selectedNode) ? _nodeBtnSel : _nodeBtn, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(140f), GUILayout.Height(44f) })) { _selectedNode = i; } if (i < NodeNames.Length - 1) { GUILayout.Label("<color=#333> › </color>", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(16f) }); } } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } private string GetNodeSummary(int node) { if (_selectedIsVariant && _selVariant >= 0 && _selVariant < _variants.Count) { SpellVariantDef spellVariantDef = _variants[_selVariant]; switch (node) { case 0: return string.IsNullOrEmpty(spellVariantDef.VoiceCommand) ? "—" : spellVariantDef.VoiceCommand; case 1: { GateDef? gate = spellVariantDef.Gate; return (gate != null && gate.Mode == GateMode.Delegate) ? ("→ " + (spellVariantDef.Gate.DelegateTo ?? "?")) : (spellVariantDef.Gate?.Mode.ToString() ?? "OwnCharges"); } case 2: { ActivationDef? activation = spellVariantDef.Activation; return (activation != null && activation.SuppressCasterVfx) ? "VFX off" : "default"; } case 3: return spellVariantDef.Targeting?.Method.ToString() ?? "None"; case 4: return (spellVariantDef.Effects.Count == 0) ? "none" : $"{spellVariantDef.Effects.Count} effect(s)"; default: return "—"; } } if (!_selectedIsVariant && _selectedIndex >= 0 && _selectedIndex < _filtered.Count) { return node switch { 0 => (_editTrigger.Length > 0) ? _editTrigger : _editKey, 1 => (_chargeFields.Count > 0) ? "editable" : "—", 2 => "read-only", 3 => "read-only", 4 => "read-only", _ => "—", }; } return "—"; } private void DrawNodePanel() { if (_selectedIsVariant && _selVariant >= 0 && _selVariant < _variants.Count) { SpellVariantDef v = _variants[_selVariant]; switch (_selectedNode) { case 0: DrawVariantVoiceNode(v); break; case 1: DrawVariantChargesNode(v); break; case 2: DrawVariantActivationNode(v); break; case 3: DrawVariantTargetingNode(v); break; case 4: DrawVariantEffectsNode(v); break; } } else if (!_selectedIsVariant && _selectedIndex >= 0 && _selectedIndex < _filtered.Count) { SpellData sd = _filtered[_selectedIndex]; switch (_selectedNode) { case 0: DrawBaseVoiceNode(sd); break; case 1: DrawBaseChargesNode(sd); break; case 2: DrawBaseActivationNode(sd); break; case 3: DrawBaseTargetingNode(sd); break; case 4: DrawBaseEffectsNode(sd); break; } } } private void DrawBaseVoiceNode(SpellData sd) { PipelineSection("Voice"); GUILayout.Space(6f); GUILayout.Label("Voice Key <size=10><color=#555>internal command identifier</color></size>", _subLabel, Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); _editKey = GUILayout.TextField(_editKey, Array.Empty<GUILayoutOption>()); if (GUILayout.Button("Assign", _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { AssignKey(sd, _editKey); } GUILayout.EndHorizontal(); GUILayout.Space(12f); GUILayout.Label("Trigger <size=10><color=#555>what you say — hyphens or spaces</color></size>", _subLabel, Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); _editTrigger = GUILayout.TextField(_editTrigger, Array.Empty<GUILayoutOption>()); if (GUILayout.Button("Apply", _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { ApplyTrigger(_editKey, _editTrigger); } GUILayout.EndHorizontal(); } private void DrawBaseChargesNode(SpellData sd) { //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_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0168: 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_01a3: 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_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_0246: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) PipelineSection("Charges"); GUILayout.Space(6f); if (_chargeFields.Count == 0) { GUILayout.Label(" <color=#555>No charge fields found on this SpellData.</color>", _subLabel, Array.Empty<GUILayoutOption>()); return; } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("<color=#555>Field</color>", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(200f) }); GUILayout.Label("<color=#555>Current</color>", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); GUILayout.Label("<color=#555>New value</color>", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.EndHorizontal(); DrawSeparator(); GUILayout.Space(2f); for (int i = 0; i < _chargeFields.Count; i++) { (FieldInfo field, string draft, string live) tuple = _chargeFields[i]; FieldInfo item = tuple.field; string item2 = tuple.draft; string item3 = tuple.live; bool flag = item2 != item3; GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(item.Name, _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(200f) }); 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) { _chargeFields[i] = (item, text, item3); } GUILayout.EndHorizontal(); } GUILayout.Space(10f); bool num = _chargeFields.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) })) { ApplyChargeFields(sd); LoadChargeFields(sd); } GUI.enabled = true; GUI.backgroundColor = backgroundColor2; } private void DrawBaseActivationNode(SpellData sd) { PipelineSection("Activation"); GUILayout.Space(6f); GUILayout.Label(" <color=#555>Activation behavior is defined by the base spell component.</color>", _subLabel, Array.Empty<GUILayoutOption>()); GUILayout.Space(8f); ReadOnlyRow("Component type", GetSpellComponentType(sd)); ReadOnlyRow("Cast animation", GetCastAnimationType(sd)); GUILayout.Space(12f); GUILayout.Label(" <color=#555>Create a variant to suppress caster VFX on activation.</color>", _subLabel, Array.Empty<GUILayoutOption>()); } private void DrawBaseTargetingNode(SpellData sd) { PipelineSection("Targeting"); GUILayout.Space(6f); GUILayout.Label(" <color=#555>Targeting is baked into the base spell component.</color>", _subLabel, Array.Empty<GUILayoutOption>()); GUILayout.Space(8f); ReadOnlyRow("Component type", GetSpellComponentType(sd)); GUILayout.Space(12f); GUILayout.Label(" <color=#555>Create a variant to override with SphereCast or Raycast targeting.</color>", _subLabel, Array.Empty<GUILayoutOption>()); } private void DrawBaseEffectsNode(SpellData sd) { PipelineSection("Effects"); GUILayout.Space(6f); GUILayout.Label(" <color=#555>Effects are handled by the base spell component.</color>", _subLabel, Array.Empty<GUILayoutOption>()); GUILayout.Space(8f); ReadOnlyRow("Component type", GetSpellComponentType(sd)); GUILayout.Space(12f); GUILayout.Label(" <color=#555>Create a variant to add data-driven effects (e.g. TriggerSpell).</color>", _subLabel, Array.Empty<GUILayoutOption>()); } private void DrawVariantVoiceNode(SpellVariantDef v) { PipelineSection("Voice / Identity"); GUILayout.Space(6f); v.VoiceCommand = PipelineRow("Voice command", v.VoiceCommand); GUILayout.Label(" <color=#555>The phrase you speak to cast. e.g. UP-BLAST</color>", _subLabel, Array.Empty<GUILayoutOption>()); GUILayout.Space(8f); v.BaseSpellKey = PipelineRow("Base spell key", v.BaseSpellKey); GUILayout.Label(" <color=#555>Voice key of the Unity component to clone. e.g. SPELL_BAS_UPDRAFT</color>", _subLabel, Array.Empty<GUILayoutOption>()); GUILayout.Space(8f); ReadOnlyRow("LocKey", v.LocKey); } private void DrawVariantChargesNode(SpellVariantDef v) { PipelineSection("Gate <size=10><color=#555>when the spell can fire</color></size>"); GUILayout.Space(4f); if (v.Gate == null) { GateDef gateDef = (v.Gate = new GateDef()); } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(" Mode:", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); v.Gate.Mode = EnumToggle(v.Gate.Mode); GUILayout.EndHorizontal(); if (v.Gate.Mode == GateMode.Delegate) { GUILayout.Space(4f); v.Gate.DelegateTo = PipelineRow("Delegate to", v.Gate.DelegateTo ?? ""); _draftMinCharges = PipelineRow("Min charges", _draftMinCharges); } GUILayout.Space(12f); PipelineSection("Charge <size=10><color=#555>which pool to consume</color></size>"); GUILayout.Space(4f); bool flag = v.Charge != null; bool flag2 = EnableToggle("Configure charge override", flag); if (flag2 != flag) { v.Charge = (flag2 ? new ChargeDef() : null); } if (v.Charge != null) { GUILayout.Space(4f); v.Charge.Source = PipelineRow("Source spell", v.Charge.Source); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(" Consume on:", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }); v.Charge.ConsumeOn = EnumToggle(v.Charge.ConsumeOn); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(" On miss:", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }); v.Charge.OnMiss = EnumToggle(v.Charge.OnMiss); GUILayout.EndHorizontal(); } } private void DrawVariantActivationNode(SpellVariantDef v) { PipelineSection("Activation <size=10><color=#555>on cast</color></size>"); GUILayout.Space(6f); bool flag = v.Activation != null; bool flag2 = EnableToggle("Configure activation override", flag); if (flag2 != flag) { v.Activation = (flag2 ? new ActivationDef() : null); } if (v.Activation != null) { GUILayout.Space(4f); bool flag3 = EnableToggle("Suppress caster VFX", v.Activation.SuppressCasterVfx); if (flag3 != v.Activation.SuppressCasterVfx) { v.Activation.SuppressCasterVfx = flag3; } } } private void DrawVariantTargetingNode(SpellVariantDef v) { PipelineSection("Targeting <size=10><color=#555>who gets hit</color></size>"); GUILayout.Space(6f); if (v.Targeting == null) { TargetingDef targetingDef = (v.Targeting = new TargetingDef()); } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(" Method:", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); v.Targeting.Method = EnumToggle(v.Targeting.Method); GUILayout.EndHorizontal(); if (v.Targeting.Method != TargetingMethod.None) { GUILayout.Space(4f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(" Radius:", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); _draftRadius = GUILayout.TextField(_draftRadius, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(55f) }); GUILayout.Space(14f); GUILayout.Label("Max dist:", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(66f) }); _draftMaxDist = GUILayout.TextField(_draftMaxDist, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(55f) }); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(" Filter:", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); v.Targeting.Filter = EnumToggle(v.Targeting.Filter); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); v.Targeting.ClipToWalls = BoolToggle(" Clip walls", v.Targeting.ClipToWalls); GUILayout.Space(10f); v.Targeting.ExcludeSelf = BoolToggle(" Excl. self", v.Targeting.ExcludeSelf); GUILayout.Space(10f); v.Targeting.ExcludeDecoys = BoolToggle(" Excl. decoys", v.Targeting.ExcludeDecoys); GUILayout.EndHorizontal(); } } private void DrawVariantEffectsNode(SpellVariantDef v) { //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: 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_013b: Unknown result type (might be due to invalid IL or missing references) PipelineSection("Effects <size=10><color=#555>what happens on hit</color></size>"); GUILayout.Space(6f); for (int num = v.Effects.Count - 1; num >= 0; num--) { EffectDef effectDef = v.Effects[num]; GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(" <b>" + effectDef.Type + "</b>", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }); if (effectDef.Type == "TriggerSpell") { GUILayout.Label("spell:", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(38f) }); effectDef.SpellKey = GUILayout.TextField(effectDef.SpellKey ?? "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) }); } GUILayout.FlexibleSpace(); effectDef.Route = EnumToggle(effectDef.Route); Color backgroundColor = GUI.backgroundColor; GUI.backgroundColor = new Color(0.55f, 0.18f, 0.18f); if (GUILayout.Button("✕", _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(24f) })) { v.Effects.RemoveAt(num); } GUI.backgroundColor = backgroundColor; GUILayout.EndHorizontal(); } GUILayout.Space(6f); if (_addingEffect) { DrawAddEffectPanel(v); } else if (GUILayout.Button("+ Add Effect", _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) })) { _addingEffect = true; _addEffectTypeIdx = 0; _addEffectSpellKey = ""; } } private void DrawVariantFooter() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) string[] variantErrors = _variantErrors; foreach (string text in variantErrors) { GUILayout.Label(" <color=#e06060>⚠ " + text + "</color>", _subLabel, Array.Empty<GUILayoutOption>()); } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); Color backgroundColor = GUI.backgroundColor; GUI.backgroundColor = new Color(0.22f, 0.52f, 0.22f); if (GUILayout.Button("Apply", _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { ApplyVariant(_variants[_selVariant]); } GUI.backgroundColor = backgroundColor; GUILayout.Space(6f); if (GUILayout.Button("Save JSON", _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { SpellInterpreter.SaveVariants(); } GUILayout.Space(6f); if (GUILayout.Button("Reload JSON", _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) })) { SpellInterpreter.ReloadVariants(); _variants = SpellInterpreter.GetAllVariants().ToList(); SelectVariant(-1); } GUILayout.EndHorizontal(); } private void StartWizard() { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) _wizardActive = true; _wizardStep = 0; _wizardError = ""; _addingEffect = false; _draftMinCharges = "1"; _wizardRadius = "0.50"; _wizardMaxDist = "20.0"; _newLocKey = "SPELL_CUSTOM_"; _newBaseKey = ""; _newVoiceCmd = ""; _wizardDef = new SpellVariantDef { Gate = new GateDef { Mode = GateMode.OwnCharges }, Targeting = new TargetingDef { Method = TargetingMethod.None } }; _variantDetailScroll = Vector2.zero; } private void CancelWizard() { _wizardActive = false; _wizardStep = 0; _wizardError = ""; _addingEffect = false; } private void AdvanceWizard() { //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) _wizardError = ""; switch (_wizardStep) { case 0: { string key = _newLocKey.Trim().ToUpperInvariant(); if (string.IsNullOrEmpty(key)) { _wizardError = "Internal key is required."; return; } if (string.IsNullOrWhiteSpace(_newBaseKey)) { _wizardError = "Base spell key is required."; return; } if (string.IsNullOrWhiteSpace(_newVoiceCmd)) { _wizardError = "Voice command is required."; return; } if (SpellInterpreter.GetAllVariants().Any((SpellVariantDef v) => v.LocKey == key)) { _wizardError = "'" + key + "' already exists."; return; } _wizardDef.LocKey = key; _wizardDef.BaseSpellKey = _newBaseKey.Trim(); _wizardDef.VoiceCommand = _newVoiceCmd.Trim().ToUpperInvariant(); break; } case 1: { GateDef? gate = _wizardDef.Gate; if (gate != null && gate.Mode == GateMode.Delegate && string.IsNullOrWhiteSpace(_wizardDef.Gate.DelegateTo)) { _wizardError = "Delegate mode requires a parent spell key."; return; } if (_wizardDef.Gate != null && int.TryParse(_draftMinCharges, out var result3)) { _wizardDef.Gate.MinCharges = result3; } break; } case 2: if (_wizardDef.Targeting != null) { if (float.TryParse(_wizardRadius, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { _wizardDef.Targeting.Radius = result; } if (float.TryParse(_wizardMaxDist, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { _wizardDef.Targeting.MaxDistance = result2; } } break; } _wizardStep++; _variantDetailScroll = Vector2.zero; } private void FinishWizard() { _wizardError = ""; if (!SpellInterpreter.UpdateVariant(_wizardDef, out var result)) { _wizardError = ((result.Errors.Count > 0) ? result.Errors[0] : "Validation failed."); return; } _variants = SpellInterpreter.GetAllVariants().ToList(); int i = _variants.FindIndex((SpellVariantDef v) => v.LocKey == _wizardDef.LocKey); CancelWizard(); SelectVariant(i); } private void DrawWizard() { //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.FlexibleSpace(); for (int i = 0; i < WizardStepNames.Length; i++) { GUILayout.Label((i < _wizardStep) ? ("<color=#4a4>✓ " + WizardStepNames[i] + "</color>") : ((i == _wizardStep) ? ("<b>" + WizardStepNames[i] + "</b>") : ("<color=#444>" + WizardStepNames[i] + "</color>")), _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(76f) }); if (i < WizardStepNames.Length - 1) { GUILayout.Label("<color=#333>›</color>", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(12f) }); } } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); DrawSeparator(); GUILayout.Space(6f); _variantDetailScroll = GUILayout.BeginScrollView(_variantDetailScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); switch (_wizardStep) { case 0: DrawWizardIdentity(); break; case 1: DrawWizardGate(); break; case 2: DrawWizardTargeting(); break; case 3: DrawWizardEffects(); break; case 4: DrawWizardReview(); break; } GUILayout.EndScrollView(); GUILayout.Space(6f); DrawSeparator(); GUILayout.Space(4f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (_wizardStep > 0 && GUILayout.Button("◀ Back", _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { _wizardStep--; _wizardError = ""; _variantDetailScroll = Vector2.zero; } GUILayout.FlexibleSpace(); if (!string.IsNullOrEmpty(_wizardError)) { GUILayout.Label("<color=#e06060>" + _wizardError + "</color>", _subLabel, Array.Empty<GUILayoutOption>()); } GUILayout.FlexibleSpace(); if (GUILayout.Button("Cancel", _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { CancelWizard(); } GUILayout.Space(6f); if (_wizardStep < 4) { if (GUILayout.Button("Next ▶", _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { AdvanceWizard(); } } else { Color backgroundColor = GUI.backgroundColor; GUI.backgroundColor = new Color(0.22f, 0.52f, 0.22f); if (GUILayout.Button("Create Spell", _tab, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) })) { FinishWizard(); } GUI.backgroundColor = backgroundColor; } GUILayout.EndHorizontal(); } private void DrawWizardIdentity() { GUILayout.Label("<b>Step 1 — Identity</b>", _header, Array.Empty<GUILayoutOption>()); GUILayout.Space(4f); GUILayout.Label("A variant reuses an existing spell's Unity component but defines its own\nvoice command, gating, targeting, and effects through data — no code required.", _subLabel, Array.Empty<GUILayoutOption>()); GUILayout.Space(14f); _newLocKey = PipelineRow("Internal key", _newLocKey); GUILayout.Label(" <color=#555>Unique identifier. Convention: SPELL_<SCHOOL>_<NAME> e.g. SPELL_ARC_VOID_BOLT</color>", _subLabel, Array.Empty<GUILayoutOption>()); GUILayout.Space(6f); _newBaseKey = PipelineRow("Base spell key", _newBaseKey); GUILayout.Label(" <color=#555>Voice key of the spell whose Unity component to clone e.g. SPELL_BAS_UPDRAFT</color>", _subLabel, Array.Empty<GUILayoutOption>()); GUILayout.Space(6f); _newVoiceCmd = PipelineRow("Voice command", _newVoiceCmd); GUILayout.Label(" <color=#555>The phrase you speak to cast. Hyphens separate syllables e.g. UP-BLAST</color>", _subLabel, Array.Empty<GUILayoutOption>()); } private void DrawWizardGate() { GUILayout.Label("<b>Step 2 — Gate</b>", _header, Array.Empty<GUILayoutOption>()); GUILayout.Space(4f); GUILayout.Label("The gate controls whether the spell is allowed to activate when you speak the command.", _subLabel, Array.Empty<GUILayoutOption>()); GUILayout.Space(14f); SpellVariantDef wizardDef = _wizardDef; if (wizardDef.Gate == null) { GateDef gateDef = (wizardDef.Gate = new GateDef()); } DrawWizardOption(_wizardDef.Gate.Mode == GateMode.OwnCharges, "Own charges", "Uses its own independent charge pool. Good for spells with their own cooldown.", delegate { _wizardDef.Gate.Mode = GateMode.OwnCharges; }); DrawWizardOption(_wizardDef.Gate.Mode == GateMode.Delegate, "Delegate", "Borrows another spell's charges. Only fires when the parent has enough charges.", delegate { _wizardDef.Gate.Mode = GateMode.Delegate; }); DrawWizardOption(_wizardDef.Gate.Mode == GateMode.Always, "Always", "No charge check — always fires when spoken. No cooldown protection.", delegate { _wizardDef.Gate.Mode = GateMode.Always; }); if (_wizardDef.Gate.Mode == GateMode.Delegate) { GUILayout.Space(10f); _wizardDef.Gate.DelegateTo = PipelineRow("Parent spell key", _wizardDef.Gate.DelegateTo ?? ""); GUILayout.Label(" <color=#555>Voice key of the spell whose charges to borrow e.g. SPELL_BAS_UPDRAFT</color>", _subLabel, Array.Empty<GUILayoutOption>()); GUILayout.Space(4f); _draftMinCharges = PipelineRow("Min charges required", _draftMinCharges); } } private void DrawWizardTargeting() { GUILayout.Label("<b>Step 3 — Targeting</b>", _header, Array.Empty<GUILayoutOption>()); GUILayout.Space(4f); GUILayout.Label("Choose how the spell locates a pawn to apply effects to when you cast it.", _subLabel, Array.Empty<GUILayoutOption>()); GUILayout.Space(14f); SpellVariantDef wizardDef = _wizardDef; if (wizardDef.Targeting == null) { TargetingDef targetingDef = (wizardDef.Targeting = new TargetingDef()); } DrawWizardOption(_wizardDef.Targeting.Method == TargetingMethod.None, "None", "No targeting. Activates without seeking a hit target.", delegate { _wizardDef.Targeting.Method = TargetingMethod.None; }); DrawWizardOption(_wizardDef.Targeting.Method == TargetingMethod.SphereCast, "Sphere cast", "A sphere sweeps forward from your gaze — hits the first valid pawn in the swept volume.", delegate { _wizardDef.Targeting.Method = TargetingMethod.SphereCast; }); DrawWizardOption(_wizardDef.Targeting.Method == TargetingMethod.Raycast, "Raycast", "A precise ray — only hits exactly what you're looking directly at.", delegate { _wizardDef.Targeting.Method = TargetingMethod.Raycast; }); if (_wizardDef.Targeting.Method != TargetingMethod.None) { GUILayout.Space(10f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(" Radius:", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); _wizardRadius = GUILayout.TextField(_wizardRadius, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(55f) }); GUILayout.Space(14f); GUILayout.Label("Max dist:", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(66f) }); _wizardMaxDist = GUILayout.TextField(_wizardMaxDist, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(55f) }); GUILayout.EndHorizontal(); GUILayout.Space(4f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(" Filter:", _subLabel, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); _wizardDef.Targeting.Filter = EnumToggle(_wizardDef.Targeting.Filter); GUILayout.EndHorizontal(); GUILayout.Space(4f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); _wizardDef.Targeting.ClipToWalls = BoolToggle(" Clip to walls", _wizardDef.Targeting.ClipToWalls); GUILayout.Space(10f); _wizardDef.Targeting.ExcludeSelf = BoolToggle(" Exclude self", _wizardDef.Targetin