Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of UniversalWand v0.7.0
UniversalWand.dll
Decompiled a week 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 System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Rendering; using UniversalWand.Compatibility; using UniversalWand.Core; using UniversalWand.Custom; using UniversalWand.Game; using UniversalWand.Input; using UniversalWand.Patches; using UniversalWand.Refunds; using UniversalWand.UI; using UniversalWand.Visuals; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Universal Wand")] [assembly: AssemblyDescription("A universal wand with optional custom spells. Built-in alternate casting inspired by MagicTweaks by Gaakrin.")] [assembly: AssemblyCompany("Universal Wand")] [assembly: AssemblyProduct("Universal Wand")] [assembly: AssemblyCopyright("Copyright 2026 Universal Wand contributors")] [assembly: ComVisible(false)] [assembly: Guid("5ff5f1a1-e63f-4ae6-93a6-60715ae49622")] [assembly: AssemblyFileVersion("0.7.0.0")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("0.7.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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 UniversalWand { public static class UniversalWandApi { public const int ApiVersion = 1; private static SpellRegistry _registry; public static event Action<SpellDefinition> SpellRegistered; internal static void Initialize(SpellRegistry registry) { _registry = registry; } public static bool RegisterSpell(SpellDefinition definition) { if (_registry == null) { return false; } _registry.Register(definition); UniversalWandApi.SpellRegistered?.Invoke(definition); return true; } } [BepInPlugin("com.universalwand.valheim", "Universal Wand", "0.7.0")] [BepInDependency("com.jotunn.jotunn", "2.30.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class UniversalWandPlugin : BaseUnityPlugin { public const string PluginGuid = "com.universalwand.valheim"; public const string PluginName = "Universal Wand"; public const string PluginVersion = "0.7.0"; private Harmony _harmony; private SpellRegistry _registry; private SpellRuntimeRegistry _runtime; private StaffsOfFracturingCompatibility _staffsOfFracturing; private UniversalWandItemService _items; private SpellSelectionController _controller; private WandFeedback _feedback; private WandCarrierService _carriers; private WandNetworkVisualService _networkVisuals; private bool _reportedNetworkFailure; private CustomSpellNetwork _customNetwork; private CustomSpellController _custom; private bool _reportedCustomNetworkFailure; private WandRefundService _refunds; private ConfigEntry<WandAppearance> _appearance; private bool _reportedRefundFailure; private void Awake() { //IL_0053: 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_0060: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown //IL_0410: Unknown result type (might be due to invalid IL or missing references) //IL_041a: Expected O, but got Unknown _registry = VanillaSpellCatalog.CreateRegistry(); _runtime = new SpellRuntimeRegistry(_registry); _staffsOfFracturing = new StaffsOfFracturingCompatibility(); ConfigEntry<CustomSpellToggle> setting = ((BaseUnityPlugin)this).Config.Bind<CustomSpellToggle>("Custom Spells", "Custom Added Spells", CustomSpellToggle.Off, new ConfigDescription("Enable Lumos and Telekinesis. Off by default. The server controls this setting in multiplayer; custom spells are always locked Off on a server without Universal Wand. In a local world you may choose On or Off.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); ConfigEntry<bool> alternateCasting = ((BaseUnityPlugin)this).Config.Bind<bool>("Casting", "Enable Alternate Casting", true, "Built-in wand right-click: Trollstav when Dead Raiser is selected, otherwise Protection. Requires the alternate spell's unlock and uses its recorded level. Disable to use the selected spell's native secondary attack. MagicTweaks is not required."); UnlockService unlockService = new UnlockService(); ConfigEntry<bool> unlockAllForTesting = ((BaseUnityPlugin)this).Config.Bind<bool>("Testing", "Unlock All Spells", false, "Temporarily makes every resolved wand spell available at level 1 without changing saved character progression. Disable after testing."); SpellAvailabilityService availability = new SpellAvailabilityService(_runtime, unlockService, () => unlockAllForTesting.Value, WandNetworkPolicy.Supports); WandStateStore state = new WandStateStore(); WandCastingViewService views = new WandCastingViewService(_runtime); WandCastQualityService wandCastQualityService = new WandCastQualityService(); VanillaAttackExecutor executor = new VanillaAttackExecutor(views, wandCastQualityService); SourceStaffProgressionScanner progression = new SourceStaffProgressionScanner(_registry, _runtime, unlockService, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); }); unlockService.Progressed += delegate(Player player, string spellId, int quality) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Recorded Universal Wand progression: " + spellId + " level " + quality + ".")); }; _feedback = new WandFeedback(); _items = new UniversalWandItemService(delegate(string message) { ((BaseUnityPlugin)this).Logger.LogInfo((object)message); }, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); }); _appearance = ((BaseUnityPlugin)this).Config.Bind<WandAppearance>("Visuals", "Wand Appearance", WandAppearance.SilverCore, "SilverCore: wood, shiny silver base and faintly glowing purple core tip (default). ClassicWood: original plain wood. Local visual preference; applies immediately."); UniversalWandVisualFactory.SetAppearance(_appearance.Value); _appearance.SettingChanged += OnAppearanceChanged; _refunds = new WandRefundService(_items, Path.Combine(Paths.ConfigPath, "UniversalWand", "recipe-refunds"), delegate(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); }); _carriers = new WandCarrierService(() => (!((Object)(object)ObjectDB.instance != (Object)null)) ? null : ObjectDB.instance.GetItemPrefab("StaffFireball"), () => _items.Identity); _networkVisuals = new WandNetworkVisualService(_registry, state, availability, _items, wandCastQualityService); WandSelectionService wandSelectionService = new WandSelectionService(_registry, availability, state, executor, _items, _feedback, () => alternateCasting.Value, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogInfo((object)message); }, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); }, delegate(Player player) { _custom?.LeaveCustom(player); }, delegate(Player player) { _custom?.CancelQueued(player); }); _customNetwork = new CustomSpellNetwork(setting, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogInfo((object)message); }); _custom = new CustomSpellController(_items, wandCastQualityService, _customNetwork, delegate(string message) { _feedback.ShowCustom(message); }, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogError((object)message); }, wandSelectionService.CancelQueued); HotkeyConfig hotkeys = new HotkeyConfig(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); _controller = new SpellSelectionController(hotkeys, wandSelectionService, _items, progression, wandCastQualityService, null, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogError((object)message); }, delegate(Player player) { _custom.Cycle(player); }, (Player player) => _custom.IsSelected(player)); PatchContext.Registry = _registry; PatchContext.Unlocks = unlockService; PatchContext.CastQualities = wandCastQualityService; PatchContext.ProjectileSnapshots = new WandProjectileSnapshotService(); PatchContext.Carriers = _carriers; PatchContext.NetworkVisuals = _networkVisuals; PatchContext.Progression = progression; PatchContext.Items = _items; PatchContext.Selection = wandSelectionService; PatchContext.CustomSpells = _custom; PatchContext.LogError = delegate(string message) { ((BaseUnityPlugin)this).Logger.LogError((object)message); }; UniversalWandApi.Initialize(_registry); UniversalWandApi.SpellRegistered += OnSpellRegistered; LocalizationManager.Instance.AddToken("item_universalwand", "Universal Wand", false); LocalizationManager.Instance.AddToken("item_universalwand_description", "One wand for the magic weapons you have personally unlocked.", false); ItemManager.OnVanillaItemsAvailable += OnVanillaItemsAvailable; ItemManager.OnItemsRegistered += OnItemsRegistered; _harmony = new Harmony("com.universalwand.valheim"); _harmony.PatchAll(typeof(UniversalWandPlugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Universal Wand 0.7.0 initialized."); } private void Start() { string installedNotice = MagicTweaksNotice.GetInstalledNotice(); if (installedNotice != null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)installedNotice); } } private void OnVanillaItemsAvailable() { _runtime.Resolve(ObjectDB.instance, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); }); _items.Initialize(); } private void OnSpellRegistered(SpellDefinition definition) { if ((Object)(object)ObjectDB.instance != (Object)null) { _runtime.Resolve(ObjectDB.instance, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); }); } } private void OnItemsRegistered() { try { if (_staffsOfFracturing != null) { _staffsOfFracturing.TryRegister(_registry, ObjectDB.instance, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogInfo((object)message); }, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); }); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Optional Staffs of Fracturing compatibility failed without disabling the base wand: " + ex)); } if ((Object)(object)ObjectDB.instance != (Object)null) { _runtime.Resolve(ObjectDB.instance, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); }); } } private void Update() { try { _refunds?.Tick(); _reportedRefundFailure = false; } catch (Exception ex) { if (!_reportedRefundFailure) { ((BaseUnityPlugin)this).Logger.LogError((object)("Wand refund failed safely; casting remains available: " + ex)); } _reportedRefundFailure = true; } try { _customNetwork?.Tick(); _reportedCustomNetworkFailure = false; } catch (Exception ex2) { if (!_reportedCustomNetworkFailure) { ((BaseUnityPlugin)this).Logger.LogError((object)("Custom spell synchronization failed: " + ex2)); } _reportedCustomNetworkFailure = true; _customNetwork?.FailClosed(); } if (_controller != null) { _controller.Tick(); } _custom?.Tick(); try { _networkVisuals?.Tick(Player.m_localPlayer); _reportedNetworkFailure = false; } catch (Exception ex3) { if (!_reportedNetworkFailure) { ((BaseUnityPlugin)this).Logger.LogError((object)("Universal Wand equipment synchronization failed and will retry: " + ex3)); } _reportedNetworkFailure = true; } } private void OnDestroy() { if (_appearance != null) { _appearance.SettingChanged -= OnAppearanceChanged; } _custom?.Dispose(); _customNetwork?.Dispose(); ItemManager.OnVanillaItemsAvailable -= OnVanillaItemsAvailable; ItemManager.OnItemsRegistered -= OnItemsRegistered; UniversalWandApi.SpellRegistered -= OnSpellRegistered; if (_harmony != null) { _harmony.UnpatchSelf(); } if (_feedback != null) { _feedback.Dispose(); } if (PatchContext.CastQualities != null) { PatchContext.CastQualities.RestoreAll(); } PatchContext.Registry = null; PatchContext.Unlocks = null; PatchContext.CastQualities = null; PatchContext.ProjectileSnapshots = null; PatchContext.Carriers = null; PatchContext.NetworkVisuals = null; PatchContext.Progression = null; PatchContext.Items = null; PatchContext.Selection = null; PatchContext.CustomSpells = null; PatchContext.LogError = null; UniversalWandVisualFactory.ReleaseUnusedResources(); } private void OnAppearanceChanged(object sender, EventArgs args) { UniversalWandVisualFactory.SetAppearance(_appearance.Value); } } } namespace UniversalWand.Visuals { internal enum WandAppearance { SilverCore, ClassicWood } internal static class UniversalWandVisualFactory { private sealed class VisualAssets { internal Mesh Mesh; internal Material Wood; internal Material Silver; internal Material Core; } private struct Geometry { internal Vector3 Direction; internal float Length; } private const int RadialSegments = 3; private static readonly List<VisualAssets> OwnedAssets = new List<VisualAssets>(); private static readonly float[] RingT = new float[5] { 0f, 0.24f, 0.52f, 0.78f, 1f }; private static readonly float[] RingRadius = new float[5] { 0.018f, 0.017f, 0.0155f, 0.0135f, 0.011f }; internal static WandAppearance Appearance { get; private set; } = WandAppearance.SilverCore; internal static void SetAppearance(WandAppearance appearance) { Appearance = ((appearance == WandAppearance.ClassicWood) ? WandAppearance.ClassicWood : WandAppearance.SilverCore); WandAppearanceVisual[] array = Resources.FindObjectsOfTypeAll<WandAppearanceVisual>(); for (int i = 0; i < array.Length; i++) { if ((Object)(object)array[i] != (Object)null && !array[i].IsBeingDestroyed) { array[i].ApplyAppearance(); } } ReleaseUnusedResources(array); } internal static void ReleaseUnusedResources() { ReleaseUnusedResources(Resources.FindObjectsOfTypeAll<WandAppearanceVisual>()); } private static void ReleaseUnusedResources(WandAppearanceVisual[] models) { for (int num = OwnedAssets.Count - 1; num >= 0; num--) { VisualAssets visualAssets = OwnedAssets[num]; bool flag = false; foreach (WandAppearanceVisual wandAppearanceVisual in models) { if ((Object)(object)wandAppearanceVisual != (Object)null && !wandAppearanceVisual.IsBeingDestroyed && (Object)(object)wandAppearanceVisual.OwnedMesh == (Object)(object)visualAssets.Mesh) { flag = true; break; } } if (!flag) { if ((Object)(object)visualAssets.Mesh != (Object)null) { Object.Destroy((Object)(object)visualAssets.Mesh); } if ((Object)(object)visualAssets.Wood != (Object)null) { Object.Destroy((Object)(object)visualAssets.Wood); } if ((Object)(object)visualAssets.Silver != (Object)null) { Object.Destroy((Object)(object)visualAssets.Silver); } if ((Object)(object)visualAssets.Core != (Object)null) { Object.Destroy((Object)(object)visualAssets.Core); } OwnedAssets.RemoveAt(num); } } } internal static void ReplaceVisual(GameObject prefab, Action<string> logWarning) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_006c: 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_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: 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) //IL_0120: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)prefab == (Object)null)) { Transform val = prefab.transform.Find("attach") ?? prefab.transform; Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true); Material source = FindSourceMaterial(componentsInChildren); Geometry geometry = Measure(componentsInChildren, val); HideOriginalVisual(prefab); GameObject val2 = new GameObject("UniversalWandModel"); val2.transform.SetParent(val, false); MeshFilter obj = val2.AddComponent<MeshFilter>(); MeshRenderer renderer = val2.AddComponent<MeshRenderer>(); VisualAssets visualAssets = new VisualAssets { Mesh = BuildMesh(Vector3.zero, geometry.Direction, geometry.Length), Wood = CreateMaterial(source, logWarning), Silver = CreateAccentMaterial(source, isCore: false, logWarning), Core = CreateAccentMaterial(source, isCore: true, logWarning) }; OwnedAssets.Add(visualAssets); obj.sharedMesh = visualAssets.Mesh; val2.AddComponent<WandAppearanceVisual>().Initialize(visualAssets.Mesh, renderer, visualAssets.Wood, visualAssets.Silver, visualAssets.Core); GameObject val3 = new GameObject("WandTip"); val3.transform.SetParent(val2.transform, false); val3.transform.localPosition = geometry.Direction * geometry.Length; } } internal static void RestoreDropVisual(GameObject drop, GameObject wandPrefab) { if (!((Object)(object)drop == (Object)null) && !((Object)(object)wandPrefab == (Object)null)) { Transform val = wandPrefab.transform.Find("attach/UniversalWandModel") ?? wandPrefab.transform.Find("UniversalWandModel"); Transform val2 = drop.transform.Find("attach") ?? drop.transform; if (!((Object)(object)val == (Object)null) && !((Object)(object)val2.Find("UniversalWandModel") != (Object)null)) { HideOriginalVisual(drop); ((Object)Object.Instantiate<GameObject>(((Component)val).gameObject, val2, false)).name = "UniversalWandModel"; } } } private static void HideOriginalVisual(GameObject item) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) Renderer[] componentsInChildren = item.GetComponentsInChildren<Renderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].enabled = false; } ParticleSystem[] componentsInChildren2 = item.GetComponentsInChildren<ParticleSystem>(true); for (int j = 0; j < componentsInChildren2.Length; j++) { MainModule main = componentsInChildren2[j].main; ((MainModule)(ref main)).playOnAwake = false; componentsInChildren2[j].Stop(true, (ParticleSystemStopBehavior)0); } } private static Material FindSourceMaterial(Renderer[] renderers) { for (int i = 0; i < renderers.Length; i++) { Material sharedMaterial = renderers[i].sharedMaterial; if ((Object)(object)sharedMaterial != (Object)null) { return sharedMaterial; } } return null; } private static Material CreateMaterial(Material source, Action<string> logWarning) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) Material val; if ((Object)(object)source != (Object)null) { val = new Material(source); } else { Shader val2 = Shader.Find("Standard"); if ((Object)(object)val2 == (Object)null) { logWarning?.Invoke("No compatible shader was found for the procedural wand visual."); return null; } val = new Material(val2); } ((Object)val).name = "UniversalWandWood"; Color val3 = default(Color); ((Color)(ref val3))..ctor(0.34f, 0.13f, 0.045f, 1f); if (val.HasProperty("_MainTex")) { val.SetTexture("_MainTex", (Texture)(object)Texture2D.whiteTexture); } if (val.HasProperty("_BaseMap")) { val.SetTexture("_BaseMap", (Texture)(object)Texture2D.whiteTexture); } if (val.HasProperty("_BumpMap")) { val.SetTexture("_BumpMap", (Texture)null); } if (val.HasProperty("_Color")) { val.SetColor("_Color", val3); } if (val.HasProperty("_BaseColor")) { val.SetColor("_BaseColor", val3); } if (val.HasProperty("_Glossiness")) { val.SetFloat("_Glossiness", 0.08f); } if (val.HasProperty("_Smoothness")) { val.SetFloat("_Smoothness", 0.08f); } if (val.HasProperty("_Metallic")) { val.SetFloat("_Metallic", 0f); } if (val.HasProperty("_EmissionColor")) { val.SetColor("_EmissionColor", Color.black); } return val; } private static Material CreateAccentMaterial(Material source, bool isCore, Action<string> logWarning) { //IL_001e: 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_005b: 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) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) Shader val = Shader.Find("Standard"); Material val2 = (Material)((!((Object)(object)val != (Object)null)) ? ((object)CreateMaterial(source, logWarning)) : ((object)new Material(val))); if ((Object)(object)val2 == (Object)null) { return null; } ((Object)val2).name = (isCore ? "UniversalWandBlackCore" : "UniversalWandSilver"); Color val3 = (isCore ? new Color(0.075f, 0.018f, 0.13f, 1f) : new Color(0.72f, 0.76f, 0.82f, 1f)); Color val4 = (Color)(isCore ? new Color(0.16f, 0.025f, 0.3f, 1f) : Color.black); if (val2.HasProperty("_Color")) { val2.SetColor("_Color", val3); } if (val2.HasProperty("_BaseColor")) { val2.SetColor("_BaseColor", val3); } if (val2.HasProperty("_Metallic")) { val2.SetFloat("_Metallic", isCore ? 0f : 1f); } if (val2.HasProperty("_Glossiness")) { val2.SetFloat("_Glossiness", isCore ? 0.22f : 0.78f); } if (val2.HasProperty("_Smoothness")) { val2.SetFloat("_Smoothness", isCore ? 0.22f : 0.78f); } if (val2.HasProperty("_EmissionColor")) { val2.SetColor("_EmissionColor", val4); } if (isCore) { val2.EnableKeyword("_EMISSION"); } else { val2.DisableKeyword("_EMISSION"); } val2.globalIlluminationFlags = (MaterialGlobalIlluminationFlags)0; return val2; } private static Geometry Measure(Renderer[] renderers, Transform attach) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_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) //IL_00f0: 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_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0131: 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_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) List<Vector3> list = new List<Vector3>(); Vector3 val = default(Vector3); for (int i = 0; i < renderers.Length; i++) { if (renderers[i] is MeshRenderer || renderers[i] is SkinnedMeshRenderer) { Bounds bounds = renderers[i].bounds; Vector3 min = ((Bounds)(ref bounds)).min; Vector3 max = ((Bounds)(ref bounds)).max; for (int j = 0; j < 8; j++) { ((Vector3)(ref val))..ctor(((j & 1) == 0) ? min.x : max.x, ((j & 2) == 0) ? min.y : max.y, ((j & 4) == 0) ? min.z : max.z); list.Add(attach.InverseTransformPoint(val)); } } } if (list.Count == 0) { return new Geometry { Direction = Vector3.up, Length = 0.41f }; } Vector3 val2 = list[0]; Vector3 val3 = list[0]; for (int k = 1; k < list.Count; k++) { val2 = Vector3.Min(val2, list[k]); val3 = Vector3.Max(val3, list[k]); } Vector3 val4 = val3 - val2; int num = ((!(val4.x >= val4.y) || !(val4.x >= val4.z)) ? ((val4.y >= val4.z) ? 1 : 2) : 0); Vector3 val5 = (val2 + val3) * 0.5f; Vector3 val6 = (Vector3)(num switch { 1 => Vector3.up, 0 => Vector3.right, _ => Vector3.forward, }); float num2 = num switch { 1 => val2.y, 0 => val2.x, _ => val2.z, }; float num3 = num switch { 1 => val3.y, 0 => val3.x, _ => val3.z, }; Vector3 val7 = val5; Vector3 val8 = val5; switch (num) { case 0: val7.x = num2; val8.x = num3; break; case 1: val7.y = num2; val8.y = num3; break; default: val7.z = num2; val8.z = num3; break; } bool flag = ((Vector3)(ref val7)).sqrMagnitude <= ((Vector3)(ref val8)).sqrMagnitude; float num4 = Mathf.Max(0.55f, num3 - num2); return new Geometry { Direction = (flag ? val6 : (-val6)), Length = Mathf.Clamp(num4 * 0.34f, 0.34f, 0.525f) }; } private static Mesh BuildMesh(Vector3 origin, Vector3 direction, float length) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_027e: 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_030c: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_031c: Unknown result type (might be due to invalid IL or missing references) //IL_0325: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Unknown result type (might be due to invalid IL or missing references) //IL_0334: Unknown result type (might be due to invalid IL or missing references) //IL_033b: Expected O, but got Unknown ((Vector3)(ref direction)).Normalize(); Vector3 val = Vector3.Cross(direction, Vector3.up); if (((Vector3)(ref val)).sqrMagnitude < 0.001f) { val = Vector3.Cross(direction, Vector3.right); } ((Vector3)(ref val)).Normalize(); Vector3 val2 = Vector3.Cross(direction, val); Vector3 normalized = ((Vector3)(ref val2)).normalized; Vector3[] array = (Vector3[])(object)new Vector3[RingT.Length * 3]; int[] array2 = new int[(RingT.Length - 1) * 3 * 6 + 6]; for (int i = 0; i < RingT.Length; i++) { float num = RingT[i]; Vector3 val3 = origin + direction * (num * length); for (int j = 0; j < 3; j++) { float num2 = (float)j * MathF.PI * 2f / 3f; float num3 = 1f + Mathf.Sin((float)j * 1.91f + (float)i * 0.83f) * 0.018f; array[i * 3 + j] = val3 + (val * Mathf.Cos(num2) + normalized * Mathf.Sin(num2)) * RingRadius[i] * num3; } } int num4 = 0; for (int k = 0; k < RingT.Length - 1; k++) { for (int l = 0; l < 3; l++) { int num5 = (l + 1) % 3; int num6 = k * 3 + l; int num7 = k * 3 + num5; int num8 = (k + 1) * 3 + l; int num9 = (k + 1) * 3 + num5; array2[num4++] = num6; array2[num4++] = num8; array2[num4++] = num7; array2[num4++] = num7; array2[num4++] = num8; array2[num4++] = num9; } } array2[num4++] = 0; array2[num4++] = 2; array2[num4++] = 1; int num10 = (RingT.Length - 1) * 3; array2[num4++] = num10; array2[num4++] = num10 + 1; array2[num4++] = num10 + 2; Vector3[] array3 = (Vector3[])(object)new Vector3[array2.Length]; int[] array4 = new int[array2.Length]; for (int m = 0; m < array2.Length; m++) { array3[m] = array[array2[m]]; array4[m] = m; } Mesh val4 = new Mesh { name = "UniversalWandTriangularStick", vertices = array3, triangles = array4 }; int num11 = 18; int num12 = (RingT.Length - 1) * num11; int[] array5 = new int[num11 + 3]; int[] array6 = new int[2 * num11]; int[] array7 = new int[num11 + 3]; Array.Copy(array4, 0, array5, 0, num11); Array.Copy(array4, num12, array5, num11, 3); Array.Copy(array4, num11, array6, 0, 2 * num11); Array.Copy(array4, 3 * num11, array7, 0, num11); Array.Copy(array4, num12 + 3, array7, num11, 3); val4.subMeshCount = 3; val4.SetTriangles(array5, 0); val4.SetTriangles(array6, 1); val4.SetTriangles(array7, 2); val4.RecalculateNormals(); val4.RecalculateBounds(); return val4; } } internal sealed class WandAppearanceVisual : MonoBehaviour { [SerializeField] private Mesh _ownedMesh; [SerializeField] private MeshRenderer _renderer; [SerializeField] private Material[] _classic; [SerializeField] private Material[] _silverCore; private bool _destroying; internal Mesh OwnedMesh => _ownedMesh; internal bool IsBeingDestroyed => _destroying; internal void Initialize(Mesh mesh, MeshRenderer renderer, Material wood, Material silver, Material core) { _ownedMesh = mesh; _renderer = renderer; _classic = (Material[])(object)new Material[3] { wood, wood, wood }; _silverCore = (Material[])(object)new Material[3] { silver, wood, core }; ApplyAppearance(); } internal void ApplyAppearance() { if (!_destroying && !((Object)(object)_renderer == (Object)null) && _silverCore != null && _classic != null) { ((Renderer)_renderer).sharedMaterials = ((UniversalWandVisualFactory.Appearance == WandAppearance.ClassicWood) ? _classic : _silverCore); } } private void OnEnable() { ApplyAppearance(); } private void OnDestroy() { _destroying = true; UniversalWandVisualFactory.ReleaseUnusedResources(); } } internal static class WandIconLoader { private const string ResourceName = "UniversalWand.Resources.wand_icon.png"; private static Sprite _cached; internal static Sprite LoadEmbeddedIcon() { //IL_0075: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_cached != (Object)null) { return _cached; } using Stream stream = typeof(WandIconLoader).Assembly.GetManifestResourceStream("UniversalWand.Resources.wand_icon.png"); if (stream == null) { return null; } byte[] array = new byte[stream.Length]; int num; for (int i = 0; i < array.Length; i += num) { num = stream.Read(array, i, array.Length - i); if (num <= 0) { return null; } } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false, false) { name = "UniversalWandInventoryIcon", hideFlags = (HideFlags)61 }; if (!ImageConversion.LoadImage(val, array, false)) { Object.Destroy((Object)(object)val); return null; } ((Texture)val).wrapMode = (TextureWrapMode)1; _cached = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f); ((Object)_cached).name = "UniversalWandInventoryIcon"; return _cached; } } } namespace UniversalWand.UI { internal sealed class WandFeedback { private GameObject _flashObject; private ParticleSystem _flash; internal void ShowSelected(Player player, SpellDefinition definition, int quality) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) ShowMessage(definition.DisplayName + " — Level " + quality); Flash(player, new Color(definition.Red, definition.Green, definition.Blue, 1f)); } internal void ShowLocked() { ShowMessage("Not yet unlocked."); } internal void ShowNoSpells() { ShowMessage("No spells unlocked."); } internal void ShowAlternateUnavailable() { ShowMessage("Alternate spell unavailable."); } internal void ShowSourceUnavailable() { ShowMessage("Spell source unavailable."); } internal void ShowCustom(string text) { ShowMessage(text); } private static void ShowMessage(string text) { if (!GUIManager.IsHeadless() && !((Object)(object)MessageHud.instance == (Object)null)) { MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true); } } private void Flash(Player player, Color color) { //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) if (GUIManager.IsHeadless() || (Object)(object)player == (Object)null) { return; } EnsureFlash(); Transform val = null; VisEquipment visEquipment = ((Humanoid)player).GetVisEquipment(); if ((Object)(object)visEquipment != (Object)null && (Object)(object)visEquipment.m_rightHand != (Object)null) { val = visEquipment.m_rightHand; Transform[] componentsInChildren = ((Component)visEquipment.m_rightHand).GetComponentsInChildren<Transform>(); foreach (Transform val2 in componentsInChildren) { if (((Object)val2).name == "WandTip" && (Object)(object)val2.parent != (Object)null && ((Object)val2.parent).name == "UniversalWandModel") { val = val2; break; } } } _flashObject.transform.position = (((Object)(object)val != (Object)null) ? val.position : (((Component)player).transform.position + ((Component)player).transform.forward * 0.45f + Vector3.up * 1.25f)); MainModule main = _flash.main; ((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(color); _flash.Stop(true, (ParticleSystemStopBehavior)0); _flash.Emit(10); } private void EnsureFlash() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0094: 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_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_flash != (Object)null)) { _flashObject = new GameObject("UniversalWandSwitchFlash") { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)(object)_flashObject); _flash = _flashObject.AddComponent<ParticleSystem>(); MainModule main = _flash.main; ((MainModule)(ref main)).loop = false; ((MainModule)(ref main)).playOnAwake = false; ((MainModule)(ref main)).duration = 0.18f; ((MainModule)(ref main)).startLifetime = MinMaxCurve.op_Implicit(0.18f); ((MainModule)(ref main)).startSpeed = MinMaxCurve.op_Implicit(0.35f); ((MainModule)(ref main)).startSize = MinMaxCurve.op_Implicit(0.055f); ((MainModule)(ref main)).maxParticles = 24; EmissionModule emission = _flash.emission; ((EmissionModule)(ref emission)).enabled = false; ShapeModule shape = _flash.shape; ((ShapeModule)(ref shape)).shapeType = (ParticleSystemShapeType)0; ((ShapeModule)(ref shape)).radius = 0.035f; } } internal void Dispose() { if ((Object)(object)_flashObject != (Object)null) { Object.Destroy((Object)(object)_flashObject); } _flashObject = null; _flash = null; } } } namespace UniversalWand.Refunds { internal static class WandCraftScope { private sealed class Scope : IDisposable { private bool _disposed; public void Dispose() { if (!_disposed) { _disposed = true; _depth--; } } } [ThreadStatic] private static int _depth; internal static IDisposable Enter() { _depth++; return new Scope(); } internal static void StampIfCreating(bool isWand, IDictionary<string, string> data) { if (_depth > 0 && isWand) { WandRefundPolicy.StampCurrentRecipe(data); } } } internal sealed class RefundMaterial { internal readonly string Prefab; internal readonly int Amount; internal RefundMaterial(string prefab, int amount) { Prefab = prefab; Amount = amount; } } internal static class WandRefundPolicy { internal const string RecipeKey = "universalwand.recipe.revision"; internal const string CurrentRecipe = "2"; internal const string CharacterClaimKey = "universalwand.oldrecipe.refund.v1"; internal const string ItemClaimKey = "universalwand.oldrecipe.refunded.v1"; internal static readonly IReadOnlyList<RefundMaterial> Materials = Array.AsReadOnly(new RefundMaterial[4] { new RefundMaterial("YggdrasilWood", 20), new RefundMaterial("Eitr", 30), new RefundMaterial("BlackCore", 5), new RefundMaterial("GemstoneRed", 2) }); internal static bool Eligible(RefundCandidate candidate) { if (candidate != null && candidate.IsWand && !candidate.Cheated && candidate.PlayerId != 0L && candidate.PlayerId == candidate.ProfileId && candidate.RecordedCrafter == candidate.PlayerId && candidate.ItemData != null && candidate.CharacterData != null && !candidate.ItemData.ContainsKey("universalwand.recipe.revision") && !candidate.ItemData.ContainsKey("universalwand.oldrecipe.refunded.v1")) { return !candidate.CharacterData.ContainsKey("universalwand.oldrecipe.refund.v1"); } return false; } internal static void StampCurrentRecipe(IDictionary<string, string> data) { if (data != null) { data["universalwand.recipe.revision"] = "2"; } } internal static void MarkClaimed(RefundCandidate candidate, string state) { candidate.CharacterData["universalwand.oldrecipe.refund.v1"] = state; candidate.ItemData["universalwand.oldrecipe.refunded.v1"] = candidate.PlayerId.ToString(CultureInfo.InvariantCulture); } } internal sealed class RefundCandidate { internal bool IsWand; internal bool Cheated; internal long PlayerId; internal long ProfileId; internal long RecordedCrafter; internal IDictionary<string, string> ItemData; internal IDictionary<string, string> CharacterData; } internal enum RefundReservation { Reserved, AlreadyClaimed } internal enum RefundOutcome { NotEligible, Deferred, AlreadyClaimed, Complete, Failed } internal sealed class RefundResult { internal RefundOutcome Outcome; internal int Added; internal int Dropped; internal Exception Error; } internal interface IRefundReceipt : IDisposable { void Complete(int added, int dropped); void Failed(int added, int dropped); } internal interface IRefundLedger { RefundReservation Reserve(long playerId, out IRefundReceipt receipt); } internal interface IRefundDestination { bool Prepare(IReadOnlyList<RefundMaterial> materials); bool CanAddOne(RefundMaterial material); bool AddOne(RefundMaterial material); void Drop(RefundMaterial material, int amount); } internal sealed class WandRefundEngine { private readonly IRefundLedger _ledger; private bool _busy; internal WandRefundEngine(IRefundLedger ledger) { _ledger = ledger; } internal RefundResult TryRefund(RefundCandidate candidate, IRefundDestination destination) { RefundResult refundResult = new RefundResult { Outcome = RefundOutcome.NotEligible }; if (_busy) { refundResult.Outcome = RefundOutcome.Deferred; return refundResult; } if (!WandRefundPolicy.Eligible(candidate)) { return refundResult; } _busy = true; IRefundReceipt receipt = null; bool flag = false; try { refundResult.Outcome = RefundOutcome.Deferred; if (destination == null || !destination.Prepare(WandRefundPolicy.Materials)) { return refundResult; } if (_ledger.Reserve(candidate.PlayerId, out receipt) == RefundReservation.AlreadyClaimed) { WandRefundPolicy.MarkClaimed(candidate, "claimed"); refundResult.Outcome = RefundOutcome.AlreadyClaimed; return refundResult; } flag = true; if (receipt == null) { throw new InvalidOperationException("Refund ledger reserved without a receipt; delivery blocked."); } WandRefundPolicy.MarkClaimed(candidate, "claimed"); foreach (RefundMaterial material in WandRefundPolicy.Materials) { int num = material.Amount; while (num > 0 && destination.CanAddOne(material)) { if (!destination.AddOne(material)) { throw new InvalidOperationException("Refund inventory insertion was rejected after its capacity check; stopped without retrying or dropping that unit."); } num--; refundResult.Added++; } if (num > 0) { destination.Drop(material, num); refundResult.Dropped += num; } } receipt.Complete(refundResult.Added, refundResult.Dropped); WandRefundPolicy.MarkClaimed(candidate, "complete"); refundResult.Outcome = RefundOutcome.Complete; } catch (Exception error) { refundResult.Error = error; refundResult.Outcome = ((!flag) ? RefundOutcome.Deferred : RefundOutcome.Failed); if (flag && receipt != null) { try { receipt.Failed(refundResult.Added, refundResult.Dropped); } catch { } } } finally { if (receipt != null) { try { receipt.Dispose(); } catch { } } _busy = false; } return refundResult; } } internal sealed class WandRefundLedger : IRefundLedger { private sealed class Receipt : IRefundReceipt, IDisposable { private readonly FileStream _stream; internal Receipt(FileStream stream) { _stream = stream; } public void Complete(int added, int dropped) { Record("complete", added, dropped); } public void Failed(int added, int dropped) { Record("stopped-no-retry", added, dropped); } private void Record(string status, int added, int dropped) { WriteDurable(_stream, "status=" + status + "\nconfirmed-inventory=" + added.ToString(CultureInfo.InvariantCulture) + "\nconfirmed-ground=" + dropped.ToString(CultureInfo.InvariantCulture) + "\n"); } public void Dispose() { _stream.Dispose(); } } private readonly string _directory; internal WandRefundLedger(string directory) { _directory = directory; } public RefundReservation Reserve(long playerId, out IRefundReceipt receipt) { receipt = null; if (playerId == 0L) { throw new ArgumentOutOfRangeException("playerId"); } Directory.CreateDirectory(_directory); string path = Path.Combine(_directory, "old-recipe-v1-" + playerId.ToString(CultureInfo.InvariantCulture) + ".receipt"); FileStream fileStream; try { fileStream = new FileStream(path, FileMode.CreateNew, FileAccess.Write, FileShare.None); } catch (IOException) { if (File.Exists(path)) { return RefundReservation.AlreadyClaimed; } throw; } try { WriteDurable(fileStream, "UniversalWand old recipe refund v1\nplayer=" + playerId.ToString(CultureInfo.InvariantCulture) + "\nstatus=claimed\nmaterials=YggdrasilWood:20,Eitr:30,BlackCore:5,GemstoneRed:2\n"); receipt = new Receipt(fileStream); return RefundReservation.Reserved; } catch { try { fileStream.Dispose(); } catch { } throw; } } private static void WriteDurable(FileStream stream, string text) { byte[] bytes = Encoding.UTF8.GetBytes(text); stream.Write(bytes, 0, bytes.Length); stream.Flush(flushToDisk: true); } } internal sealed class WandRefundService { private sealed class Destination : IRefundDestination { private readonly Player _player; private readonly Inventory _inventory; private readonly Dictionary<string, ItemData> _templates = new Dictionary<string, ItemData>(StringComparer.Ordinal); private Vector3 _dropPoint; internal Destination(Player player, Inventory inventory) { _player = player; _inventory = inventory; } public bool Prepare(IReadOnlyList<RefundMaterial> materials) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) _dropPoint = ((Component)_player).transform.position + Vector3.up * 0.25f; if (!Finite(_dropPoint.x) || !Finite(_dropPoint.y) || !Finite(_dropPoint.z)) { return false; } foreach (RefundMaterial material in materials) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(material.Prefab); ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null); ItemData val2 = (((Object)(object)val != (Object)null) ? val.m_itemData : null); if (val2 == null || val2.m_shared == null || val2.m_shared.m_maxStackSize < 1 || (Object)(object)itemPrefab.GetComponent<ZNetView>() == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ZNetScene.instance.GetPrefab(material.Prefab) == (Object)null) { return false; } val2 = val2.Clone(); val2.m_dropPrefab = itemPrefab; val2.m_stack = 1; val2.m_quality = 1; val2.m_variant = 0; val2.m_crafterID = 0L; val2.m_crafterName = ""; val2.m_cheated = false; val2.m_customData = new Dictionary<string, string>(StringComparer.Ordinal); _templates.Add(material.Prefab, val2); } return true; } public bool CanAddOne(RefundMaterial material) { return _inventory.CanAddItem(_templates[material.Prefab], 1); } public bool AddOne(RefundMaterial material) { ItemData val = _templates[material.Prefab].Clone(); val.m_stack = 1; return _inventory.AddItem(val); } public void Drop(RefundMaterial material, int amount) { //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) ItemData val = _templates[material.Prefab]; while (amount > 0) { int num = Math.Min(amount, val.m_shared.m_maxStackSize); ItemData obj = val.Clone(); obj.m_stack = num; if ((Object)(object)ItemDrop.DropItem(obj, num, _dropPoint, Quaternion.identity) == (Object)null) { throw new InvalidOperationException("Native refund drop returned no item; stopped without retrying."); } amount -= num; } } private static bool Finite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } } private readonly UniversalWandItemService _items; private readonly WandRefundEngine _engine; private readonly Action<string> _log; private readonly HashSet<long> _handled = new HashSet<long>(); private readonly HashSet<long> _reported = new HashSet<long>(); private float _nextScan; internal WandRefundService(UniversalWandItemService items, string directory, Action<string> log) { _items = items; _engine = new WandRefundEngine(new WandRefundLedger(directory)); _log = log; } internal void Tick() { if (Time.unscaledTime < _nextScan) { return; } _nextScan = Time.unscaledTime + 1f; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting() || (Object)(object)Game.instance == (Object)null || (Object)(object)Chat.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return; } ZNetView component = ((Component)localPlayer).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner()) { return; } long playerID = localPlayer.GetPlayerID(); if (playerID == 0L || _handled.Contains(playerID)) { return; } try { PlayerProfile playerProfile = Game.instance.GetPlayerProfile(); if (playerProfile == null || playerProfile.GetPlayerID() != playerID) { return; } if (localPlayer.m_customData == null) { localPlayer.m_customData = new Dictionary<string, string>(StringComparer.Ordinal); } if (localPlayer.m_customData.ContainsKey("universalwand.oldrecipe.refund.v1")) { _handled.Add(playerID); return; } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); if (inventory == null) { return; } List<ItemData> allItems = inventory.GetAllItems(); for (int i = 0; i < allItems.Count; i++) { ItemData val = allItems[i]; if (val == null || !_items.IsWand(val)) { continue; } RefundCandidate candidate = new RefundCandidate { IsWand = true, Cheated = val.m_cheated, PlayerId = playerID, ProfileId = playerProfile.GetPlayerID(), RecordedCrafter = val.m_crafterID, ItemData = val.m_customData, CharacterData = localPlayer.m_customData }; if (!WandRefundPolicy.Eligible(candidate)) { continue; } RefundResult refundResult = _engine.TryRefund(candidate, new Destination(localPlayer, inventory)); if (refundResult.Outcome == RefundOutcome.Deferred) { if (refundResult.Error != null && _reported.Add(playerID)) { _log?.Invoke("Wand refund deferred before delivery: " + refundResult.Error); } break; } if (refundResult.Outcome != RefundOutcome.NotEligible) { _handled.Add(playerID); try { Game.instance.SavePlayerProfile(false, false); } catch (Exception ex) { _log?.Invoke("Wand refund game save failed; durable claim retained: " + ex); } if (refundResult.Outcome == RefundOutcome.Complete) { ((Terminal)Chat.instance).AddString("[Universal Wand] The wand now costs fewer materials to craft. Your old crafting materials have been returned: 20 Yggdrasil wood, 30 refined eitr, 5 black cores and 2 red gemstones. Any items that did not fit are on the ground at your feet. You keep your wand."); _log?.Invoke("Wand old-recipe refund completed once: " + refundResult.Added + " items in inventory, " + refundResult.Dropped + " items on ground."); } else if (refundResult.Outcome == RefundOutcome.Failed) { _log?.Invoke("Wand refund STOPPED with its permanent claim retained; do not automatically retry. Confirmed inventory=" + refundResult.Added + ", ground=" + refundResult.Dropped + ". " + refundResult.Error); ((Terminal)Chat.instance).AddString("[Universal Wand] The wand recipe is cheaper, but your material refund stopped after an error. It will not retry automatically, to avoid duplicate items. Check the Universal Wand log before any manual recovery."); } else if (refundResult.Outcome == RefundOutcome.AlreadyClaimed) { ((Terminal)Chat.instance).AddString("[Universal Wand] A previous material-refund claim already exists for this character, so no second refund was created. If that refund was interrupted, check the Universal Wand log before any manual recovery."); } break; } } } catch (Exception ex2) { if (_reported.Add(playerID)) { _log?.Invoke("Wand refund scan failed safely: " + ex2); } } } } } namespace UniversalWand.Patches { [HarmonyPatch(typeof(Humanoid), "StartAttack")] internal static class WandCastBoundaryPatch { private static bool Prefix(Humanoid __instance, bool secondaryAttack, ref bool __result) { Player val = (Player)(object)((__instance is Player) ? __instance : null); bool flag = false; try { if ((Object)(object)val == (Object)null || PatchContext.Items == null) { return true; } if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)val != (Object)(object)Player.m_localPlayer) { return true; } ItemData currentWeapon = ((Humanoid)val).GetCurrentWeapon(); if (!PatchContext.Items.IsWand(currentWeapon)) { if (PatchContext.Progression != null) { PatchContext.Progression.RecordEquippedSource(val, currentWeapon); } return true; } flag = PatchContext.CustomSpells != null && PatchContext.CustomSpells.IsSelected(val); if (PatchContext.CustomSpells != null && PatchContext.CustomSpells.TryHandleAttack(val, currentWeapon, secondaryAttack, out var accepted, out var nativeProtection)) { if (nativeProtection) { if (PatchContext.Selection != null && PatchContext.CastQualities != null && PatchContext.Selection.PrepareProtectionCast(val, currentWeapon) && PatchContext.CastQualities.BeginCast(val, currentWeapon, PatchContext.Items)) { return true; } __result = false; return false; } __result = accepted; return false; } if (PatchContext.Selection == null || PatchContext.CastQualities == null) { return true; } if (PatchContext.Selection.PrepareCast(val, currentWeapon, secondaryAttack) && PatchContext.CastQualities.BeginCast(val, currentWeapon, PatchContext.Items)) { return true; } __result = false; return false; } catch (Exception ex) { if (PatchContext.CastQualities != null) { PatchContext.CastQualities.CompleteStart(val, accepted: false); } if (PatchContext.LogError != null) { PatchContext.LogError("Universal Wand cast preparation failed; " + (flag ? "custom input was cancelled: " : "vanilla attack processing was preserved: ") + ex); } if (flag) { __result = false; return false; } return true; } } private static void Postfix(Humanoid __instance, bool __result) { try { if (PatchContext.CastQualities != null) { PatchContext.CastQualities.CompleteStart((Player)(object)((__instance is Player) ? __instance : null), __result); } } catch (Exception ex) { if (PatchContext.LogError != null) { PatchContext.LogError("Universal Wand failed to close a rejected cast cleanly: " + ex); } } } } internal static class PatchContext { internal static SpellRegistry Registry; internal static UnlockService Unlocks; internal static WandCastQualityService CastQualities; internal static WandProjectileSnapshotService ProjectileSnapshots; internal static WandCarrierService Carriers; internal static WandNetworkVisualService NetworkVisuals; internal static SourceStaffProgressionScanner Progression; internal static UniversalWandItemService Items; internal static WandSelectionService Selection; internal static CustomSpellController CustomSpells; internal static Action<string> LogError; } internal static class WandCarrierHooks { internal static void Restore(ItemData item) { if (PatchContext.Carriers != null) { PatchContext.Carriers.RestoreLocalItem(item); } if (item != null) { WandCraftScope.StampIfCreating(WandItemIdentity.IsWand(((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null, item.m_customData), item.m_customData); } } internal static void RestoreDrop(ItemDrop drop, bool initializeNetwork) { if ((Object)(object)drop == (Object)null || PatchContext.Carriers == null || !PatchContext.Carriers.RestoreLocalItem(drop.m_itemData)) { return; } ZNetView component = ((Component)drop).GetComponent<ZNetView>(); if (initializeNetwork && (Object)(object)component != (Object)null && component.IsOwner() && component.GetZDO() != null) { ZDO zDO = component.GetZDO(); if (zDO.GetPrefab() == StringExtensionMethods.GetStableHashCode("UniversalWand")) { zDO.SetPrefab(StringExtensionMethods.GetStableHashCode("StaffFireball")); } ItemDrop.SaveToZDO(drop.m_itemData, zDO, -1); } if (!GUIManager.IsHeadless() && PatchContext.Items != null && (Object)(object)((Component)drop).transform.Find("attach/UniversalWandModel") == (Object)null && (Object)(object)((Component)drop).transform.Find("UniversalWandModel") == (Object)null) { UniversalWandVisualFactory.RestoreDropVisual(((Component)drop).gameObject, PatchContext.Items.VisualPrefab); } } } [HarmonyPatch(typeof(ZNetView), "GetPrefabName")] internal static class WandNetworkPrefabPatch { private static void Postfix(ref string __result) { if (__result == "UniversalWand") { __result = "StaffFireball"; } } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] internal static class WandInventoryAddPatch { private static void Prefix(ItemData item) { WandCarrierHooks.Restore(item); } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData), typeof(int), typeof(int), typeof(int), typeof(bool) })] internal static class WandInventoryPositionAddPatch { private static void Prefix(ItemData item) { WandCarrierHooks.Restore(item); } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData), typeof(Vector2i) })] internal static class WandInventoryGridAddPatch { private static void Prefix(ItemData item) { WandCarrierHooks.Restore(item); } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(int), typeof(int), typeof(float), typeof(Vector2i), typeof(bool), typeof(int), typeof(int), typeof(long), typeof(string), typeof(Dictionary<string, string>), typeof(int), typeof(bool), typeof(bool), typeof(bool) })] internal static class WandInventoryLegacyLoadPatch { private static void Prefix(ref int prefabHash, ref Dictionary<string, string> customData) { WandCarrierService.MigrateLegacyLoad(ref prefabHash, ref customData); } } [HarmonyPatch(typeof(ItemDrop), "Awake")] internal static class WandDropAwakePatch { private static void Postfix(ItemDrop __instance) { WandCarrierHooks.RestoreDrop(__instance, initializeNetwork: true); } } [HarmonyPatch(typeof(ItemDrop), "Load")] internal static class WandDropLoadPatch { private static void Postfix(ItemDrop __instance) { WandCarrierHooks.RestoreDrop(__instance, initializeNetwork: false); } } [HarmonyPatch(typeof(ItemDrop), "LoadFromZDO")] internal static class WandDropDataLoadPatch { private static void Postfix(ItemData itemData) { WandCarrierHooks.Restore(itemData); } } [HarmonyPatch(typeof(ItemDrop), "DropItem")] internal static class WandDropSpawnPatch { private static void Prefix(ItemData item) { if (PatchContext.Carriers != null) { PatchContext.Carriers.PrepareForNetwork(item); } } private static void Postfix(ItemDrop __result) { WandCarrierHooks.RestoreDrop(__result, initializeNetwork: false); } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(string), typeof(int), typeof(int), typeof(int), typeof(long), typeof(string), typeof(Vector2i), typeof(bool), typeof(bool), typeof(bool) })] internal static class WandCraftReceiptPatch { [HarmonyPriority(800)] private static void Prefix(string name, out IDisposable __state) { __state = ((name == "UniversalWand") ? WandCraftScope.Enter() : null); } private static void Postfix(string name, ItemData __result) { if (!(name != "UniversalWand") && __result != null) { if (__result.m_customData == null) { __result.m_customData = new Dictionary<string, string>(StringComparer.Ordinal); } WandRefundPolicy.StampCurrentRecipe(__result.m_customData); } } private static Exception Finalizer(Exception __exception, IDisposable __state) { __state?.Dispose(); return __exception; } } [HarmonyPatch(typeof(ItemData), "Clone", new Type[] { })] internal static class WandItemClonePatch { private static void Postfix(ItemData __instance, ItemData __result) { if (PatchContext.CastQualities != null) { PatchContext.CastQualities.RestoreCloneQuality(__instance, __result); } if (PatchContext.Carriers != null) { PatchContext.Carriers.PrepareForNetwork(__result); } } } [HarmonyPatch(typeof(ItemData), "Save", new Type[] { typeof(ZPackage) })] internal static class WandItemSavePatch { private static void Prefix(ItemData __instance, out IDisposable __state) { __state = null; if (PatchContext.Carriers != null) { PatchContext.Carriers.PrepareForNetwork(__instance); } __state = ((PatchContext.CastQualities != null) ? PatchContext.CastQualities.SuspendForSave(__instance) : null); } private static Exception Finalizer(Exception __exception, IDisposable __state) { __state?.Dispose(); return __exception; } } [HarmonyPatch(typeof(VisEquipment), "SetRightItem")] internal static class WandRightNetworkPatch { private static void Prefix(VisEquipment __instance, ref int itemHash, ref int quality) { PatchContext.NetworkVisuals?.MapOutgoing(__instance, back: false, ref itemHash, ref quality); } } [HarmonyPatch(typeof(VisEquipment), "SetRightItemVisual")] internal static class WandRightVisualNetworkPatch { private static void Prefix(VisEquipment __instance, ref int itemHash, ref int quality) { PatchContext.NetworkVisuals?.MapOutgoing(__instance, back: false, ref itemHash, ref quality); } } [HarmonyPatch(typeof(VisEquipment), "SetRightBackItem")] internal static class WandBackNetworkPatch { private static void Prefix(VisEquipment __instance, ref int itemHash, ref int quality) { PatchContext.NetworkVisuals?.MapOutgoing(__instance, back: true, ref itemHash, ref quality); } } [HarmonyPatch(typeof(VisEquipment), "SetRightHandEquipped")] internal static class WandRightRenderPatch { private static void Prefix(VisEquipment __instance, ref int hash, ref int quality) { PatchContext.NetworkVisuals?.MapVisual(__instance, back: false, ref hash, ref quality); } } [HarmonyPatch(typeof(VisEquipment), "SetBackEquipped")] internal static class WandBackRenderPatch { private static void Prefix(VisEquipment __instance, ref int rightItem, ref int rightQuality) { PatchContext.NetworkVisuals?.MapVisual(__instance, back: true, ref rightItem, ref rightQuality); } } internal static class WandProjectileSnapshotPatch { internal static void Capture(ref ItemData item) { try { if (PatchContext.ProjectileSnapshots != null) { item = PatchContext.ProjectileSnapshots.Capture(item, PatchContext.Items); } } catch (Exception ex) { if (PatchContext.LogError != null) { PatchContext.LogError("Universal Wand could not capture delayed spell data; native effect setup was preserved: " + ex); } } } } [HarmonyPatch(typeof(Projectile), "Setup")] internal static class WandProjectileSetupPatch { private static void Prefix(ref ItemData item) { WandProjectileSnapshotPatch.Capture(ref item); } } [HarmonyPatch(typeof(SpawnAbility), "Setup")] internal static class WandSpawnAbilitySetupPatch { private static void Prefix(ref ItemData item) { WandProjectileSnapshotPatch.Capture(ref item); } } [HarmonyPatch(typeof(Aoe), "Setup")] internal static class WandAoeSetupPatch { private static void Prefix(ref ItemData item) { WandProjectileSnapshotPatch.Capture(ref item); } } } namespace UniversalWand.Input { internal sealed class GameplayInputGate { private readonly Lazy<Func<Player, bool>> _takeInput = new Lazy<Func<Player, bool>>(BindTakeInput); internal bool Allows(Player player) { if ((Object)(object)player != (Object)null) { return _takeInput.Value(player); } return false; } private static Func<Player, bool> BindTakeInput() { MethodInfo method = typeof(Player).GetMethod("TakeInput", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method == null || method.ReturnType != typeof(bool)) { throw new MissingMethodException("Valheim Player.TakeInput() is unavailable; spell selection is disabled while gameplay input cannot be checked."); } try { return (Func<Player, bool>)Delegate.CreateDelegate(typeof(Func<Player, bool>), method); } catch (ArgumentException) { return (Player player) => (bool)method.Invoke(player, null); } catch (MemberAccessException) { return (Player player) => (bool)method.Invoke(player, null); } } } internal sealed class HotkeyConfig { private readonly ConfigEntry<KeyboardShortcut>[] _entries = new ConfigEntry<KeyboardShortcut>[10]; private readonly ConfigEntry<KeyboardShortcut> _extraSpells; private readonly ConfigEntry<KeyboardShortcut> _customSpells; private readonly ManualLogSource _log; private string _lastConflictSignature; internal KeyboardShortcut this[int slot] => _entries[slot].Value; internal KeyboardShortcut ExtraSpells => _extraSpells.Value; internal KeyboardShortcut CustomSpells => _customSpells.Value; internal HotkeyConfig(ConfigFile config, ManualLogSource log) { //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) _log = log; bool saveOnConfigSet = config.SaveOnConfigSet; config.SaveOnConfigSet = false; try { Bind(config, 0, (KeyCode)256, "Staff of Protection"); Bind(config, 1, (KeyCode)257, "Staff of Frost"); Bind(config, 2, (KeyCode)258, "Staff of Embers"); Bind(config, 3, (KeyCode)259, "Staff of Fracturing"); Bind(config, 4, (KeyCode)260, "Dundr"); Bind(config, 5, (KeyCode)261, "Staff of the Wild"); Bind(config, 6, (KeyCode)0, "Reserved Numpad 6"); Bind(config, 7, (KeyCode)0, "Reserved Numpad 7"); Bind(config, 8, (KeyCode)264, "Dead Raiser"); Bind(config, 9, (KeyCode)265, "Trollstav"); _extraSpells = config.Bind<KeyboardShortcut>("Spell Selection", "Extra Spells Key", new KeyboardShortcut((KeyCode)271, Array.Empty<KeyCode>()), "Cycle unlocked, available extra spells. Set to None to unbind. Numbered selection keys take priority if bindings overlap."); _extraSpells.SettingChanged += OnSettingChanged; _customSpells = config.Bind<KeyboardShortcut>("Spell Selection", "Custom Spells Key", new KeyboardShortcut((KeyCode)270, Array.Empty<KeyCode>()), "Cycle Lumos, Telekinesis. Spirit Orb and custom Lightning Strike are disabled. Requires Custom Added Spells On; None unbinds. Numbered keys and Extra Spells take precedence."); _customSpells.SettingChanged += OnSettingChanged; } finally { config.SaveOnConfigSet = saveOnConfigSet; if (saveOnConfigSet) { config.Save(); } } ValidateConflicts(); } internal bool IsDown(int slot) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) return IsDown(_entries[slot].Value); } internal bool IsExtraSpellsDown() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return IsDown(_extraSpells.Value); } internal bool IsCustomSpellsDown() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return IsDown(_customSpells.Value); } private static bool IsDown(KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0 || !UnityInput.Current.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey)) { return false; } foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { if (!UnityInput.Current.GetKey(modifier)) { return false; } } return true; } private void Bind(ConfigFile config, int slot, KeyCode key, string label) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut val = (KeyboardShortcut)(((int)key == 0) ? KeyboardShortcut.Empty : new KeyboardShortcut(key, Array.Empty<KeyCode>())); ConfigDefinition val2 = new ConfigDefinition("Spell Selection", "Slot " + slot); KeyboardShortcut value = config.Bind<KeyboardShortcut>(val2, val, new ConfigDescription("Legacy setting migrated automatically.", (AcceptableValueBase)null, Array.Empty<object>())).Value; config.Remove(val2); _entries[slot] = config.Bind<KeyboardShortcut>("Spell Selection", label + " Key", value, "Select " + label + ". Set to None to unbind. Other held gameplay keys no longer block selection."); _entries[slot].SettingChanged += OnSettingChanged; } private void OnSettingChanged(object sender, EventArgs args) { ValidateConflicts(); } private void ValidateConflicts() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) string[] array = new string[_entries.Length + 2]; KeyboardShortcut value; for (int i = 0; i < _entries.Length; i++) { int num = i; value = _entries[i].Value; object obj; if ((int)((KeyboardShortcut)(ref value)).MainKey != 0) { value = _entries[i].Value; obj = ((KeyboardShortcut)(ref value)).Serialize(); } else { obj = "None"; } array[num] = (string)obj; } int num2 = _entries.Length; value = _extraSpells.Value; object obj2; if ((int)((KeyboardShortcut)(ref value)).MainKey != 0) { value = _extraSpells.Value; obj2 = ((KeyboardShortcut)(ref value)).Serialize(); } else { obj2 = "None"; } array[num2] = (string)obj2; int num3 = _entries.Length + 1; value = _customSpells.Value; object obj3; if ((int)((KeyboardShortcut)(ref value)).MainKey != 0) { value = _customSpells.Value; obj3 = ((KeyboardShortcut)(ref value)).Serialize(); } else { obj3 = "None"; } array[num3] = (string)obj3; IReadOnlyList<string> values = HotkeyConflictDetector.FindDuplicates(array); string text = string.Join(";", values); if (text.Length > 0 && !string.Equals(text, _lastConflictSignature, StringComparison.Ordinal)) { _log.LogWarning((object)("Universal Wand hotkey conflict: " + text + ". Lowest slot number wins; numbered slots take priority over Extra Spells, then Custom Spells.")); } _lastConflictSignature = text; } } internal sealed class SpellSelectionController { private readonly HotkeyConfig _hotkeys; private readonly WandSelectionService _selection; private readonly UniversalWandItemService _items; private readonly SourceStaffProgressionScanner _progression; private readonly WandCastQualityService _castQualities; private readonly GameplayInputGate _inputGate; private readonly Action<string> _logError; private readonly Action<Player> _cycleCustom; private readonly Func<Player, bool> _isCustomSelected; private readonly HashSet<string> _reportedFailures = new HashSet<string>(StringComparer.Ordinal); internal SpellSelectionController(HotkeyConfig hotkeys, WandSelectionService selection, UniversalWandItemService items, SourceStaffProgressionScanner progression, WandCastQualityService castQualities, GameplayInputGate inputGate = null, Action<string> logError = null, Action<Player> cycleCustom = null, Func<Player, bool> isCustomSelected = null) { _hotkeys = hotkeys; _selection = selection; _items = items; _progression = progression; _castQualities = castQualities; _inputGate = inputGate ?? new GameplayInputGate(); _logError = logError; _cycleCustom = cycleCustom; _isCustomSelected = isCustomSelected; } internal void Tick() { Player localPlayer = Player.m_localPlayer; try { _castQualities.Tick(localPlayer); } catch (Exception error) { ReportFailure("cast-quality maintenance", error); } if ((Object)(object)localPlayer == (Object)null) { return; } try { _progression.Tick(localPlayer, Time.unscaledTime); } catch (Exception error2) { ReportFailure("progression scan", error2); } try { _selection.FlushQueued(localPlayer); if (((Character)localPlayer).IsDead() || !_inputGate.Allows(localPlayer)) { return; } ItemData currentWeapon = ((Humanoid)localPlayer).GetCurrentWeapon(); if (!_items.IsWand(currentWeapon)) { return; } for (int i = 0; i < 10; i++) { if (_hotkeys.IsDown(i)) { _selection.RequestSlot(localPlayer, i); return; } } if (_hotkeys.IsExtraSpellsDown()) { _selection.RequestGroup(localPlayer, "deep_north"); } else if (_hotkeys.IsCustomSpellsDown()) { _cycleCustom?.Invoke(localPlayer); } else if (!((Character)localPlayer).InAttack() && (_isCustomSelected == null || !_isCustomSelected(localPlayer))) { _selection.EnsureSelected(localPlayer, currentWeapon); } } catch (Exception error3) { ReportFailure("spell selection", error3); } } private void ReportFailure(string stage, Exception error) { if (_logError != null && _reportedFailures.Add(stage + ":" + error.GetType().FullName)) { _logError("Universal Wand " + stage + " failed and will retry: " + error); } } } } namespace UniversalWand.Game { internal sealed class SourceStaffProgressionScanner { private const float ScanInterval = 0.75f; private readonly SpellRegistry _registry; private readonly SpellRuntimeRegistry _runtime; private readonly UnlockService _unlocks; private readonly Action<string> _logWarning; private bool _reportedFailure; private float _nextScan; internal SourceStaffProgressionScanner(SpellRegistry registry, SpellRuntimeRegistry runtime, UnlockService unlocks, Action<string> logWarning) { _registry = registry; _runtime = runtime; _unlocks = unlocks; _logWarning = logWarning; } internal void Tick(Player player, float now) { if ((Object)(object)player == (Object)null || now < _nextScan) { return; } _nextScan = now + 0.75f; try { _unlocks.Load(player); RecordSourceItem(player, ((Humanoid)player).GetCurrentWeapon()); Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory != null) { List<ItemData> allItems = inventory.GetAllItems(); for (int i = 0; i < allItems.Count; i++) { RecordSourceItem(player, allItems[i]); } } _reportedFailure = false; } catch (Exception error) { ReportFailure(error); } } internal bool RecordEquippedSource(Player player, ItemData item) { try { return RecordSourceItem(player, item); } catch (Exception error) { ReportFailure(error); return false; } } private bool RecordSourceItem(Player player, ItemData item) { if ((Object)(object)player == (Object)null || item == null) { return false; } string prefabName = GetPrefabName(item); if (WandItemIdentity.IsWand(prefabName, item.m_customData)) { return false; } if (!_registry.TryGetSource(prefabName, out var definition) && !_runtime.TryMatchSource(item.m_shared, out definition)) { return false; } return _unlocks.Record(player, definition.Id, Math.Max(1, item.m_quality)); } internal static string GetPrefabName(ItemData item) { string text = ((item != null && (Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null); if (text == null || !text.EndsWith("(Clone)", StringComparison.Ordinal)) { return text; } return text.Substring(0, text.Length - "(Clone)".Length); } private void ReportFailure(Exception error) { if (!_reportedFailure) { _reportedFailure = true; if (_logWarning != null) { _logWarning("Source-staff progression scan failed safely and will retry: " + error); } } } } internal sealed class SpellAvailabilityService { private readonly SpellRuntimeRegistry _runtime; private readonly UnlockService _unlocks; private readonly Func<bool> _unlockAllForTesting; private readonly Func<SpellDefinition, bool> _networkSupports; internal SpellAvailabilityService(SpellRuntimeRegistry runtime, UnlockService unlocks, Func<bool> unlockAllForTesting, Func<SpellDefinition, bool> networkSupports = null) { _runtime = runtime; _unlocks = unlocks; _unlockAllForTesting = unlockAllForTesting; _networkSupports = networkSupports; } internal bool IsAvailable(Player player, SpellDefinition definition) { return GetQuality(player, definition) > 0; } internal int GetQuality(Player player, SpellDefinition definition) { if ((Object)(object)player == (Object)null || definition == null || !_runtime.IsResolved(definition.Id) || (_networkSupports != null && !_networkSupports(definition))) { return 0; } int quality = _unlocks.GetQuality(player, definition.Id); bool unlockAllForTesting = _unlockAllForTesting != null && _unlockAllForTesting(); return ProgressionOverridePlanner.GetEffectiveQuality(quality, unlockAllForTesting); } internal bool IsResolved(SpellDefinition definition) { if (definition != null) { return _runtime.IsResolved(definition.Id); } return false; } } internal sealed class SpellRuntimeRegistry { private readonly SpellRegistry _definitions; private readonly Dictionary<string, SharedData> _sources = new Dictionary<string, SharedData>(StringComparer.Ordinal); private readonly HashSet<string> _reportedMissing = new HashSet<string>(StringComparer.Ordinal); internal SpellRuntimeRegistry(SpellRegistry definitions) { _definitions = definitions; } internal void Resolve(ObjectDB objectDb, Action<string> logWarning) { if ((Object)(object)objectDb == (Object)null) { return; } _sources.Clear(); IReadOnlyList<SpellDefinition> readOnlyList = _definitions.Ordered(); for (int i = 0; i < readOnlyList.Count; i++) { SpellDefinition spellDefinition = readOnlyList[i]; if (!string.IsNullOrEmpty(spellDefinition.SourcePrefab)) { GameObject itemPrefab = objectDb.GetItemPrefab(spellDefinition.SourcePrefab); ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null); if ((Object)(object)val != (Object)null && val.m_itemData != null && val.m_itemData.m_shared != null) { _sources[spellDefinition.Id] = val.m_itemData.m_shared; } else if (_reportedMissing.Add(spellDefinition.SourcePrefab)) { logWarning?.Invoke("Vanilla source prefab is unavailable; spell disabled: " + spellDefinition.SourcePrefab); } } } } internal bool IsResolved(string spellId) { if (spellId != null) { return _sources.ContainsKey(spellId); } return false; } internal bool TryGetSource(string spellId, out SharedData shared) { if (spellId != null) { return _sources.TryGetValue(spellId, out shared); } shared = null; return false; } internal bool TryMatchSource(SharedData shared, out SpellDefinition definition) { if (shared != null) { foreach (KeyValuePair<string, SharedData> source in _sources) { if (Matches(source.Value, shared) && _definitions.TryGet(source.Key, out definition)) { return true; } } } definition = null; return false; } private static bool Matches(SharedData source, SharedData candidate) { if (source == null || candidate == null) { return false; } if (source == candidate) { return true; } if (!string.IsNullOrEmpty(source.m_name) && string.Equals(source.m_name, candidate.m_name, StringComparison.Ordinal)) { return true; } if (source.m_attack != null && source.m_attack == candidate.m_attack) { return true; } if (source.m_attack != null && candidate.m_attack != null && (Object)(object)source.m_attack.m_attackProjectile != (Object)null) { return source.m_attack.m_attackProjectile == candidate.m_attack.m_attackProjectile; } return false; } } internal sealed class UniversalWandItemService { internal const string PrefabName = "UniversalWand"; private readonly Action<string> _logInfo; private readonly Action<string> _logWarning; private bool _initialized; private CustomItem _customItem; internal GameObject VisualPrefab { get { if (_customItem == null) { return null; } return _customItem.ItemPrefab; } } internal SharedData Identity { get { if (_customItem == null || !((Object)(object)_customItem.ItemDrop != (Object)null)) { return null; } return _customItem.ItemDrop.m_itemData.m_shared; } } internal UniversalWandItemService(Action<string> logInfo, Action<string> logWarning) { _logInfo = logInfo; _logWarning = logWarning; } internal void Initialize() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown if (!_initialized) { _initialized = true; Sprite val = (GUIManager.IsHeadless() ? null : WandIconLoader.LoadEmbeddedIcon()); ItemConfig val2 = new ItemConfig(); val2.Name = "$item_universalwand"; val2.Description = "$item_universalwand_description"; val2.CraftingStation = "piece_magetable"; val2.RepairStation = "piece_magetable"; val2.MinStationLevel = 1; val2.Amount = 1; val2.Requirements = (RequirementConfig[])(object)new RequirementConfig[4] { new RequirementConfig("RoundLog", 10, 5, true), new RequirementConfig("FineWood", 5, 2, true), new RequirementConfig("Silver", 2, 1, true), new RequirementConfig("BlackCore", 1, 0, true) }; ItemConfig val3 = val2; if ((Object)(object)val != (Object)null) { val3.Icon = val; } _customItem = new CustomItem("UniversalWand", "StaffFireball", val3); ItemDrop itemDrop = _customItem.ItemDrop; if ((Object)(object)itemDrop == (Object)null || itemDrop.m_itemData == null || itemDrop.m_itemData.m_shared == null) { throw new InvalidOperationException("Jotunn did not create a valid Universal Wand item clone."); } SharedData shared = itemDrop.m_itemData.m_shared; itemDrop.m_itemData.m_customData = ((itemDrop.m_itemData.m_customData != null) ? new Dictionary<string, string>(itemDrop.m_itemData.m_customData) : new Dictionary<string, string>()); itemDrop.m_itemData.m_customData["universalwand.item.v1"] = "1"; WandRefundPolicy.StampCurrentRecipe(itemDrop.m_itemData.m_customData); shared.m_maxQuality = 4; shared.m_useDurability = true; shared.m_destroyBroken = false; shared.m_canBeReparied = true; shared.m_maxDurability = 200f; shared.m_durabilityPerLevel = 50f; shared.m_maxStackSize = 1; shared.m_weight = 1f; if (!GUIManager.IsHeadless()) { UniversalWandVisualFactory.ReplaceVisual(_customItem.ItemPrefab, _logWarning); } if (!ItemManager.Instance.AddItem(_customItem)) { throw new InvalidOperationException("Jotunn rejected Universal Wand item registration."); } if (_logInfo != null) { _logInfo("Registered Universal Wand and Galdr table recipe."); } } } internal bool IsWand(ItemData item) { if (item != null) { return WandItemIdentity.IsWand(((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null, item.m_customData); } return false; } } internal sealed class UnlockService { private sealed class PlayerProgressionCache { internal string Encoded; internal Dictionary<string, int> Qualities = new Dictionary<string, int>(StringComparer.Ordinal); } internal const string SaveKey = "universalwand.progression.v2"; private const string LegacyUnlockSuffix = ".universalwand.unlocks.v1"; private readonly ConditionalWeakTable<Player, PlayerProgressionCache> _cache = new ConditionalWeakTable<Player, PlayerProgressionCache>(); internal event Action<Player, string, int> Progressed; internal void Load(Player player) { if (!((Object)(object)player == (Object)null)) { string encoded = ReadAndMigrate(player); Refresh(_cache.GetOrCreateValue(player), encoded); } } internal bool IsUnlocked(Player player, string spellId) { return GetQuality(player, spellId) > 0; } internal int GetQuality(Player player, string spellId) { if ((Object)(object)player == (Object)null || string.IsNullOrEmpty(spellId)) { return 0; } string encoded = ReadAndMigrate(player); PlayerProgressionCache orCreateValue = _cache.GetOrCreateValue(player); Refresh(orCreateValue, encoded); if (!orCreateValue.Qualities.TryGetValue(spellId, out var value)) { return 0; } return value; } internal bool Record(Player player, string spellId, int quality) { if ((Object)(object)player == (Object)null || string.IsNullOrEmpty(spellId) || quality < 1) { return false; } quality = Math.Min(100, quality); string encoded = ReadAndMigrate(player); PlayerProgressionCache orCreateValue = _cache.GetOrCreateValue(player); Refresh(orCreateValue, encoded); if (orCreateValue.Qualities.TryGetValue(spellId, out var value) && value >= quality) { return false; } orCreateValue.Qualities[spellId] = quality; orCreateValue.Encoded = ProgressionCodec.Encode(orCreateValue.Qualities); player.m_customData["universalwand.progression.v2"] = orCreateValue.Encoded; this.Progressed?.Invoke(player, spellId, quality); return true; } private static string ReadAndMigrate(Player player) { if (player.m_customData == null) { player.m_customData = new Dictionary<string, string>(StringComparer.Ordinal); } player.m_customData.TryGetValue("universalwand.progression.v2", out var value); Dictionary<string, int> dictionary = ProgressionCodec.Decode(value); List<string> list = new List<string>(); foreach (string key in player.m_customData.Keys) { if (key != null && key.EndsWith(".universalwand.unlocks.v1", StringComparison.Ordinal)) { list.Add(key); } } bool flag = false; for (int i = 0; i < list.Count; i++) { if (player.m_customData.TryGetValue(list[i], out var value2)) { foreach (string item in StateCodec.Decode(value2)) { if (!dictionary.TryGetValue(item, out var value3) || value3 < 1) { dictionary[item] = 1; flag = true; } } } player.m_customData.Remove(list[i]); flag = true; } if (flag) { value = ProgressionCodec.Encode(dictionary); player.m_customData["universalwand.progression.v2"] = value; } return value; } private static void Refresh(PlayerProgressionCache cache, string encoded) { if (!string.Equals(cache.Encoded, encoded, StringComparison.Ordinal)) { cache.Encoded = encoded; cache.Qualities = ProgressionCodec.Decode(encoded); } } } internal sealed class VanillaAttackExecutor { private readonly WandCastingViewService _views; private readonly WandCastQualityService _qualities; internal VanillaAttackExecutor(WandCastingViewService views, WandCastQualityService qualities) { _views = views; _qualities = qualities; } internal bool Prepare(ItemData wand, SpellDefinition definition, int quality) { if (quality < 1 || !_views.Apply(wand, definition)) { return false; } _qualities.Set(wand, quality); return true; } internal bool PrepareSecondary(ItemData wand, SpellDefinition target, int targetQuality) { if (targetQuality < 1 || !_views.ApplyNativeSecondary(wand, target)) { return false; } _qualities.Set(wand, targetQuality); return true; } } internal static class VanillaSpellCatalog { internal const string ProtectionId = "vanilla.protection"; internal const string FrostId = "vanilla.frost"; internal const string EmbersId = "vanilla.embers"; internal const string Fractur