using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using BepInEx; using SideLoader; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyVersion("0.0.0.0")] namespace RunicDagger; [BepInPlugin("com.merofos.outward.runicdagger", "Runic Dagger", "0.1.8")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class RunicDaggerPlugin : BaseUnityPlugin { private const string PluginGuid = "com.merofos.outward.runicdagger"; private const string SideLoaderGuid = "com.sinai.SideLoader"; private const string PluginName = "Runic Dagger"; private const string PluginVersion = "0.1.8"; private const int RunicDaggerItemId = -2525110; private const int MaxLoadedMaterialSearchAttempts = 4; private const string RunicDaggerIconPath = "SideLoader/Items/-2525110-Runic_Dagger/icon.png"; private const string RunicDaggerIconId = "MEROFOS_RunicDaggerIcon"; private const string CapturedRunicBladeMaterialName = "mat_itm_crystalBladeRunicBladeLight"; private const string CapturedRunicBladeMainTextureName = "tex_fx_PerlinNoiseGray"; private const string CapturedRunicBladeWaveTextureName = "tex_env_waterWave_n"; private static readonly HashSet s_missingTextureWarnings = new HashSet(); private static readonly float[] VisualRetryDelays = new float[8] { 0f, 0.1f, 0.25f, 0.5f, 1f, 2f, 4f, 8f }; private static readonly FieldInfo ItemIconField = typeof(Item).GetField("m_itemIcon", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo ItemIconPathField = typeof(Item).GetField("m_itemIconPath", BindingFlags.Instance | BindingFlags.NonPublic); private static Texture s_capturedMainTexture; private static Texture s_capturedWaveTexture; private readonly Dictionary _materialCache = new Dictionary(); private readonly HashSet _pendingVisualRetries = new HashSet(); private Sprite _runicDaggerIcon; private Material _runicBladeSourceMaterial; private int _runicMaterialGeneration; private bool _capturedTexturesReady; private bool _loggedIconFailure; private bool _loggedNoRenderer; private int _loadedMaterialSearchAttempts; private static string PluginDirectory { get { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (!string.IsNullOrEmpty(directoryName)) { return directoryName; } return "."; } } private void Awake() { SL.OnPacksLoaded += OnSideLoaderPacksLoaded; SL_Item.AddOnInstanceStartListener(-2525110, (Action)OnRunicDaggerInstanceStart); TryRefreshCapturedTextureState(); TryRefreshRunicBladeSourceMaterial(allowLoadedMaterialSearch: false); Debug.Log((object)"[Runic Dagger] Loaded."); } private void OnRunicDaggerInstanceStart(Item item) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown if (!((Object)(object)item == (Object)null) && item.ItemID == -2525110) { ApplyRunicDaggerIcon(item); item.OnProcessVisual -= new ProcessVisual(OnRunicDaggerProcessVisual); item.OnProcessVisual += new ProcessVisual(OnRunicDaggerProcessVisual); QueueRunicVisualApply(item); } } private void OnSideLoaderPacksLoaded() { ApplyRunicDaggerIconToPrefab(); } private void OnRunicDaggerProcessVisual(Item item) { QueueRunicVisualApply(item); } private void QueueRunicVisualApply(Item item) { if (!((Object)(object)item == (Object)null) && item.ItemID == -2525110) { TryRefreshCapturedTextureState(); TryRefreshRunicBladeSourceMaterial(allowLoadedMaterialSearch: true); ApplyRunicVisual(item, logMissingRenderer: false); int instanceID = ((Object)item).GetInstanceID(); if (_pendingVisualRetries.Add(instanceID)) { ((MonoBehaviour)this).StartCoroutine(ApplyRunicVisualWhenReady(item, instanceID)); } } } private IEnumerator ApplyRunicVisualWhenReady(Item item, int instanceId) { bool applied = false; for (int i = 0; i < VisualRetryDelays.Length; i++) { float num = VisualRetryDelays[i]; if (num > 0f) { yield return (object)new WaitForSecondsRealtime(num); } else { yield return null; } if ((Object)(object)item == (Object)null) { break; } TryRefreshCapturedTextureState(); TryRefreshRunicBladeSourceMaterial(allowLoadedMaterialSearch: true); applied |= ApplyRunicVisual(item, logMissingRenderer: false); } if (!applied && (Object)(object)item != (Object)null) { ApplyRunicVisual(item, logMissingRenderer: true); } _pendingVisualRetries.Remove(instanceId); } private bool ApplyRunicVisual(Item item, bool logMissingRenderer) { if ((Object)(object)item == (Object)null || item.ItemID != -2525110) { return false; } if (ApplyToKnownVisualRoots(item)) { return true; } if (logMissingRenderer && !_loggedNoRenderer) { _loggedNoRenderer = true; Debug.LogWarning((object)"[Runic Dagger] Could not find a safe Rondel Dagger renderer to recolor."); } return false; } private void TryRefreshRunicBladeSourceMaterial(bool allowLoadedMaterialSearch) { if (!((Object)(object)_runicBladeSourceMaterial != (Object)null)) { Material val = LoadCapturedRunicBladeMaterial(); if ((Object)(object)val == (Object)null && allowLoadedMaterialSearch && _loadedMaterialSearchAttempts < 4) { _loadedMaterialSearchAttempts++; val = FindLoadedCapturedRunicBladeMaterial(); } if (!((Object)(object)val == (Object)null)) { _runicBladeSourceMaterial = val; _runicMaterialGeneration++; _materialCache.Clear(); Debug.Log((object)("[Runic Dagger] Captured Runic Blade material '" + ((Object)val).name + "' with shader '" + ((Object)val.shader).name + "'.")); DumpRunicBladeMaterial(val); } } } private void TryRefreshCapturedTextureState() { bool flag = CapturedRunicBladeTexturesReady(); if (flag != _capturedTexturesReady) { _capturedTexturesReady = flag; if (flag) { _runicMaterialGeneration++; _materialCache.Clear(); Debug.Log((object)"[Runic Dagger] Captured Runic Blade fallback textures are ready."); } } } private void ApplyRunicDaggerIconToPrefab() { try { if (ResourcesPrefabManager.Instance != null) { Item itemPrefab = ResourcesPrefabManager.Instance.GetItemPrefab(-2525110); ApplyRunicDaggerIcon(itemPrefab); } } catch (Exception ex) { if (!_loggedIconFailure) { _loggedIconFailure = true; Debug.LogWarning((object)("[Runic Dagger] Could not apply custom icon to prefab: " + ex.Message)); } } } private void ApplyRunicDaggerIcon(Item item) { if ((Object)(object)item == (Object)null || item.ItemID != -2525110) { return; } Sprite runicDaggerIcon = GetRunicDaggerIcon(); if ((Object)(object)runicDaggerIcon == (Object)null) { return; } try { if (ItemIconField != null) { ItemIconField.SetValue(item, runicDaggerIcon); } if (ItemIconPathField != null) { ItemIconPathField.SetValue(item, "MEROFOS_RunicDaggerIcon"); } else { item.ItemIconPath = "MEROFOS_RunicDaggerIcon"; } } catch (Exception ex) { if (!_loggedIconFailure) { _loggedIconFailure = true; Debug.LogWarning((object)("[Runic Dagger] Could not apply custom icon: " + ex.Message)); } } } private Sprite GetRunicDaggerIcon() { if ((Object)(object)_runicDaggerIcon != (Object)null) { return _runicDaggerIcon; } string text = Path.Combine(PluginDirectory, "SideLoader/Items/-2525110-Runic_Dagger/icon.png".Replace('/', Path.DirectorySeparatorChar)); try { Texture2D val = CustomTextures.LoadTexture(text, false, false); if ((Object)(object)val == (Object)null) { if (!_loggedIconFailure) { _loggedIconFailure = true; Debug.LogWarning((object)("[Runic Dagger] Could not load custom icon from " + text)); } return null; } ((Object)val).name = "MEROFOS_RunicDaggerIcon"; _runicDaggerIcon = CustomTextures.CreateSprite(val, (SpriteBorderTypes)1); ((Object)_runicDaggerIcon).name = "MEROFOS_RunicDaggerIcon"; } catch (Exception ex) { if (!_loggedIconFailure) { _loggedIconFailure = true; Debug.LogWarning((object)("[Runic Dagger] Could not create custom icon: " + ex.Message)); } } return _runicDaggerIcon; } private bool ApplyToKnownVisualRoots(Item item) { HashSet visitedRoots = new HashSet(); bool flag = false; bool flag2 = SafeIsInWorld(item); bool flag3 = SafeIsEquipped(item); if (!flag2 && !flag3) { HideKnownItemVisuals(item); return false; } ItemVisual val = SafeGetCurrentVisual(item); if ((Object)(object)val != (Object)null) { flag |= ApplyToVisualRoot(((Component)val).gameObject, visitedRoots); } ItemVisual val2 = SafeGetLoadedVisual(item); if ((Object)(object)val2 != (Object)null) { flag |= ApplyToVisualRoot(((Component)val2).gameObject, visitedRoots); } if (flag2) { flag |= ApplyToVisualRoot(((Component)item).gameObject, visitedRoots); } return flag; } private bool ApplyToVisualRoot(GameObject root, HashSet visitedRoots) { if ((Object)(object)root == (Object)null) { return false; } int instanceID = ((Object)root).GetInstanceID(); if (!visitedRoots.Add(instanceID)) { return false; } Renderer[] componentsInChildren = root.GetComponentsInChildren(true); if (!ApplyToRenderers(componentsInChildren, requireVisualName: true)) { return ApplyToRenderers(componentsInChildren, requireVisualName: false); } return true; } private static ItemVisual SafeGetCurrentVisual(Item item) { try { return item.CurrentVisual; } catch { return null; } } private static ItemVisual SafeGetLoadedVisual(Item item) { try { return item.LoadedVisual; } catch { return null; } } private static bool SafeIsInWorld(Item item) { try { return (Object)(object)item != (Object)null && item.IsInWorld; } catch { return false; } } private static bool SafeIsEquipped(Item item) { try { return (Object)(object)item != (Object)null && item.IsEquipped; } catch { return false; } } private static void HideKnownItemVisuals(Item item) { ItemVisual val = SafeGetCurrentVisual(item); if ((Object)(object)val != (Object)null) { SafeHideVisual(val); } ItemVisual val2 = SafeGetLoadedVisual(item); if ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)val) { SafeHideVisual(val2); } } private static void SafeHideVisual(ItemVisual visual) { try { visual.Hide(); } catch { } } private bool ApplyToRenderers(Renderer[] renderers, bool requireVisualName) { bool result = false; foreach (Renderer val in renderers) { if (!ShouldPatchRenderer(val, requireVisualName)) { continue; } Material[] sharedMaterials = val.sharedMaterials; if (sharedMaterials == null || sharedMaterials.Length == 0) { continue; } Material[] array = (Material[])(object)new Material[sharedMaterials.Length]; bool flag = false; for (int j = 0; j < sharedMaterials.Length; j++) { Material val2 = sharedMaterials[j]; if ((Object)(object)val2 == (Object)null || ShouldSkipMaterial(val2)) { array[j] = val2; continue; } if (IsRunicDaggerMaterial(val2) && !ShouldRefreshRunicMaterial(val2)) { array[j] = val2; continue; } array[j] = GetRunicMaterial(val2); flag = true; } if (flag) { val.sharedMaterials = array; result = true; } } return result; } private Material GetRunicMaterial(Material original) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown string text = ((Object)original).name ?? string.Empty; int instanceID = ((Object)original).GetInstanceID(); if (_materialCache.TryGetValue(instanceID, out var value) && (Object)(object)value != (Object)null) { return value; } Material val = new Material(original); ((Object)val).name = "MEROFOS_RunicDagger_" + text; Material runicBladeSourceMaterial = GetRunicBladeSourceMaterial(); if ((Object)(object)runicBladeSourceMaterial != (Object)null) { val = new Material(runicBladeSourceMaterial); ((Object)val).name = "MEROFOS_RunicDagger_Exact_" + ((Object)runicBladeSourceMaterial).name; } else { ApplyCapturedRunicBladeMaterial(val); } _materialCache[instanceID] = val; return val; } private bool ShouldRefreshRunicMaterial(Material material) { string text = ((Object)material).name ?? string.Empty; if ((Object)(object)_runicBladeSourceMaterial != (Object)null && !text.StartsWith("MEROFOS_RunicDagger_Exact_", StringComparison.Ordinal)) { return true; } if (CapturedRunicBladeTexturesReady() && text.StartsWith("MEROFOS_RunicDagger_Captured_", StringComparison.Ordinal)) { if (HasTexture(material, "_MainTex") && HasTexture(material, "_NormalMap")) { return !HasTexture(material, "_DistortMap"); } return true; } return false; } private Material GetRunicBladeSourceMaterial() { if ((Object)(object)_runicBladeSourceMaterial != (Object)null) { return _runicBladeSourceMaterial; } TryRefreshRunicBladeSourceMaterial(allowLoadedMaterialSearch: true); return _runicBladeSourceMaterial; } private static void ApplyRunicShader(Material material) { Shader val = Shader.Find("Custom/Distort/DistortTextureSpec"); if ((Object)(object)val != (Object)null) { material.shader = val; } } private static Material LoadCapturedRunicBladeMaterial() { string[] array = new string[5] { "mat_itm_crystalBladeRunicBladeLight", "Materials/mat_itm_crystalBladeRunicBladeLight", "_Materials/mat_itm_crystalBladeRunicBladeLight", "Items/mat_itm_crystalBladeRunicBladeLight", "Weapons/mat_itm_crystalBladeRunicBladeLight" }; for (int i = 0; i < array.Length; i++) { Material val = Resources.Load(array[i]); if ((Object)(object)val != (Object)null) { return val; } } return null; } private static Material FindLoadedCapturedRunicBladeMaterial() { Material[] array = Resources.FindObjectsOfTypeAll(); foreach (Material val in array) { if (!((Object)(object)val == (Object)null) && ((Object)val).name != null && !((Object)val).name.StartsWith("MEROFOS_RunicDagger_", StringComparison.Ordinal)) { string name = ((Object)val).name; if (string.Equals(name, "mat_itm_crystalBladeRunicBladeLight", StringComparison.Ordinal) || name.StartsWith("mat_itm_crystalBladeRunicBladeLight ", StringComparison.Ordinal) || name.StartsWith("mat_itm_crystalBladeRunicBladeLight(", StringComparison.Ordinal)) { return val; } } } return null; } private static void ApplyCapturedRunicBladeMaterial(Material material) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) ((Object)material).name = "MEROFOS_RunicDagger_Captured_mat_itm_crystalBladeRunicBladeLight"; material.renderQueue = 2000; ApplyRunicShader(material); SetTexture(material, "_MainTex", GetCapturedMainTexture()); SetTexture(material, "_NormalMap", GetCapturedWaveTexture()); SetTexture(material, "_DistortMap", GetCapturedWaveTexture()); SetColor(material, "_Color", new Color(14.55578f, 6.948529f, 15f, 1f)); SetColor(material, "_SpecColor", new Color(0.3455882f, 0.3455882f, 0.3455882f, 0.759f)); SetFloat(material, "_NormalStrength", 1f); SetFloat(material, "_Speed", 1f); SetFloat(material, "_Scale", 2f); SetFloat(material, "_MaskPow", 0f); material.shaderKeywords = new string[2] { "_ALPHATEST_ON", "_NORMALMAP" }; material.EnableKeyword("_ALPHATEST_ON"); material.EnableKeyword("_NORMALMAP"); } private static void DumpRunicBladeMaterial(Material material) { try { string text = Path.Combine(Paths.ConfigPath, "MEROFOS.RunicDagger.RunicBladeMaterialDump.txt"); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Runic Dagger - captured Runic Blade material"); stringBuilder.AppendLine("Generated: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); stringBuilder.AppendLine("Material: " + SafeName((Object)(object)material)); stringBuilder.AppendLine("Shader: " + (((Object)(object)material.shader == (Object)null) ? "" : SafeName((Object)(object)material.shader))); stringBuilder.AppendLine("RenderQueue: " + material.renderQueue); stringBuilder.AppendLine("Keywords: " + string.Join(", ", material.shaderKeywords ?? new string[0])); stringBuilder.AppendLine(); stringBuilder.AppendLine("[Textures]"); AppendTexture(stringBuilder, material, "_MainTex"); AppendTexture(stringBuilder, material, "_BumpMap"); AppendTexture(stringBuilder, material, "_NormalMap"); AppendTexture(stringBuilder, material, "_DistortMap"); AppendTexture(stringBuilder, material, "_EmissionMap"); stringBuilder.AppendLine(); stringBuilder.AppendLine("[Colors]"); AppendColor(stringBuilder, material, "_Color"); AppendColor(stringBuilder, material, "_SpecColor"); AppendColor(stringBuilder, material, "_EmissionColor"); AppendColor(stringBuilder, material, "_TintColor"); stringBuilder.AppendLine(); stringBuilder.AppendLine("[Floats]"); AppendFloat(stringBuilder, material, "_NormalStrength"); AppendFloat(stringBuilder, material, "_Speed"); AppendFloat(stringBuilder, material, "_Scale"); AppendFloat(stringBuilder, material, "_MaskPow"); AppendFloat(stringBuilder, material, "_EmissionIntensity"); AppendFloat(stringBuilder, material, "_EmissionPower"); AppendFloat(stringBuilder, material, "_Glow"); AppendFloat(stringBuilder, material, "_GlowIntensity"); AppendFloat(stringBuilder, material, "_Intensity"); AppendFloat(stringBuilder, material, "_Glossiness"); AppendFloat(stringBuilder, material, "_Metallic"); File.WriteAllText(text, stringBuilder.ToString()); Debug.Log((object)("[Runic Dagger] Wrote Runic Blade material dump to " + text)); } catch (Exception ex) { Debug.LogWarning((object)("[Runic Dagger] Could not write Runic Blade material dump: " + ex.Message)); } } private static void AppendTexture(StringBuilder builder, Material material, string propertyName) { if (!material.HasProperty(propertyName)) { builder.AppendLine(propertyName + " = "); return; } Texture texture = material.GetTexture(propertyName); builder.AppendLine(propertyName + " = " + (((Object)(object)texture == (Object)null) ? "" : SafeName((Object)(object)texture))); } private static void AppendColor(StringBuilder builder, Material material, string propertyName) { //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) if (!material.HasProperty(propertyName)) { builder.AppendLine(propertyName + " = "); return; } Color color = material.GetColor(propertyName); builder.AppendLine(propertyName + " = " + color.r + ", " + color.g + ", " + color.b + ", " + color.a); } private static void AppendFloat(StringBuilder builder, Material material, string propertyName) { if (!material.HasProperty(propertyName)) { builder.AppendLine(propertyName + " = "); } else { builder.AppendLine(propertyName + " = " + material.GetFloat(propertyName)); } } private static string SafeName(Object obj) { if (!(obj == (Object)null)) { return obj.name; } return ""; } private static bool CapturedRunicBladeTexturesReady() { if ((Object)(object)GetCapturedMainTexture() != (Object)null) { return (Object)(object)GetCapturedWaveTexture() != (Object)null; } return false; } private static Texture GetCapturedMainTexture() { if ((Object)(object)s_capturedMainTexture == (Object)null) { s_capturedMainTexture = FindTexture("tex_fx_PerlinNoiseGray"); } return s_capturedMainTexture; } private static Texture GetCapturedWaveTexture() { if ((Object)(object)s_capturedWaveTexture == (Object)null) { s_capturedWaveTexture = FindTexture("tex_env_waterWave_n"); } return s_capturedWaveTexture; } private static Texture FindTexture(string textureName) { string[] array = new string[6] { textureName, "Textures/" + textureName, "_Textures/" + textureName, "FX/" + textureName, "VFX/" + textureName, "Environment/" + textureName }; for (int i = 0; i < array.Length; i++) { Texture val = Resources.Load(array[i]); if ((Object)(object)val != (Object)null) { return val; } } Texture[] array2 = Resources.FindObjectsOfTypeAll(); foreach (Texture val2 in array2) { if ((Object)(object)val2 != (Object)null && string.Equals(((Object)val2).name, textureName, StringComparison.Ordinal)) { return val2; } } if (!s_missingTextureWarnings.Contains(textureName)) { s_missingTextureWarnings.Add(textureName); Debug.LogWarning((object)("[Runic Dagger] Could not find texture '" + textureName + "' for captured Runic Blade material.")); } return null; } private static void SetColor(Material material, string propertyName, Color value) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (material.HasProperty(propertyName)) { material.SetColor(propertyName, value); } } private static void SetFloat(Material material, string propertyName, float value) { if (material.HasProperty(propertyName)) { material.SetFloat(propertyName, value); } } private static void SetTexture(Material material, string propertyName, Texture texture) { if ((Object)(object)texture != (Object)null && material.HasProperty(propertyName)) { material.SetTexture(propertyName, texture); } } private static bool HasTexture(Material material, string propertyName) { if (material.HasProperty(propertyName)) { return (Object)(object)material.GetTexture(propertyName) != (Object)null; } return false; } private static bool IsRunicDaggerMaterial(Material material) { if ((Object)(object)material != (Object)null && ((Object)material).name != null) { return ((Object)material).name.StartsWith("MEROFOS_RunicDagger_", StringComparison.Ordinal); } return false; } private static bool ShouldPatchRenderer(Renderer renderer, bool requireVisualName) { if ((Object)(object)renderer == (Object)null || renderer is TrailRenderer || renderer is LineRenderer || renderer is ParticleSystemRenderer) { return false; } if (requireVisualName && !HasRunicDaggerVisualName(((Component)renderer).transform)) { return false; } string text = ((((Object)renderer).name == null) ? string.Empty : ((Object)renderer).name.ToLowerInvariant()); if (!text.Contains("highlight") && !text.Contains("trail") && !text.Contains("linecast") && !text.Contains("gateframe") && !text.Contains("hair") && !text.Contains("head")) { return !text.Contains("face"); } return false; } private static bool ShouldSkipMaterial(Material material) { string text = ((((Object)material).name == null) ? string.Empty : ((Object)material).name.ToLowerInvariant()); if (!text.Contains("hair") && !text.Contains("skin") && !text.Contains("head") && !text.Contains("face") && !text.Contains("beard")) { return text.StartsWith("mat_cha_", StringComparison.Ordinal); } return true; } private static bool HasRunicDaggerVisualName(Transform transform) { Transform val = transform; while ((Object)(object)val != (Object)null) { string text = ((((Object)val).name == null) ? string.Empty : ((Object)val).name.ToLowerInvariant()); if (text.Contains("5110000") || text.Contains("rondeldagger") || text.Contains("rondel dagger") || text.Contains("runicdagger") || text.Contains("runic dagger") || text.Contains("-2525110") || text.Contains("2525110")) { return true; } val = val.parent; } return false; } }