using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using SideLoader; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyVersion("0.0.0.0")] namespace RunicBladeLongerDuration; [BepInPlugin("com.codex.outward.runicbladelongerduration", "Runic Blade Longer Duration", "1.3.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class RunicBladeLongerDurationPlugin : BaseUnityPlugin { private class LegacyConfig { public bool HasDurationSeconds; public float DurationSeconds = 604800f; public bool HasRunicBladeCountsAsLexicon; public bool RunicBladeCountsAsLexicon = true; public bool HasRequireArcaneSyntaxForRunicBladeLexicon; public bool RequireArcaneSyntaxForRunicBladeLexicon; } private sealed class ConfigurationManagerAttributes { public Action CustomDrawer { get; set; } public string DispName { get; set; } public int? Order { get; set; } } [HarmonyPatch(typeof(RunicBlade), "ActivateLocally")] private static class RunicBladeActivateLocallyPatch { private static void Prefix(RunicBlade __instance) { if ((Object)(object)__instance != (Object)null) { __instance.SummonLifeSpan = s_durationSeconds; } } } [HarmonyPatch(typeof(SummonedEquipment), "Activate")] private static class SummonedEquipmentActivatePatch { private static void Prefix(SummonedEquipment __instance, ref float _lifespan) { if (IsRunicSummonedEquipment(__instance) && _lifespan < s_durationSeconds) { _lifespan = s_durationSeconds; } } } [HarmonyPatch(typeof(SummonedEquipment), "RefreshLifespan")] private static class SummonedEquipmentRefreshLifespanPatch { private static void Prefix(SummonedEquipment __instance, ref float _lifeSpan) { if (IsRunicSummonedEquipment(__instance) && _lifeSpan < s_durationSeconds) { _lifeSpan = s_durationSeconds; } } } [HarmonyPatch(typeof(Item), "HasTag", new Type[] { typeof(Tag) })] private static class ItemHasTagPatch { private static void Postfix(Item __instance, Tag _tag, ref bool __result) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (IsRunicBlade(__instance) && IsLexiconTag(_tag)) { if (!RunicBladeLexiconAllowed(__instance)) { __result = false; } else if (!__result) { __result = true; } } } } [HarmonyPatch(typeof(Item), "HasTag", new Type[] { typeof(IList), typeof(bool) })] private static class ItemHasTagListPatch { private static void Postfix(Item __instance, IList _tags, bool _perfectMatch, ref bool __result) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (!IsRunicBlade(__instance) || _tags == null) { return; } bool flag = false; for (int i = 0; i < _tags.Count; i++) { if (IsLexiconTag(_tags[i])) { flag = true; break; } } if (flag && (!_perfectMatch || _tags.Count == 1)) { if (!RunicBladeLexiconAllowed(__instance)) { __result = false; } else if (!__result) { __result = true; } } } } private const string PluginGuid = "com.codex.outward.runicbladelongerduration"; private const string SideLoaderGuid = "com.sinai.SideLoader"; private const string PluginName = "Runic Blade Longer Duration"; private const string PluginVersion = "1.3.1"; private const int RunicBladeItemId = 2000100; private const int GreatRunicBladeItemId = 2100999; private const int ArcaneSyntaxSkillId = 8205180; private const float VanillaDurationSeconds = 180f; private const float DefaultDurationSeconds = 604800f; private const bool DefaultRunicBladeCountsAsLexicon = true; private const bool DefaultRequireArcaneSyntaxForRunicBladeLexicon = false; private static readonly string[] LexiconTag = new string[1] { "Lexicon" }; private static float s_durationSeconds = 604800f; private static bool s_runicBladeCountsAsLexicon = true; private static bool s_requireArcaneSyntaxForRunicBladeLexicon = false; private readonly HashSet _extendedSummonUids = new HashSet(); private ConfigEntry _durationSecondsConfig; private ConfigEntry _runicBladeCountsAsLexiconConfig; private ConfigEntry _requireArcaneSyntaxForRunicBladeLexiconConfig; private float _durationSeconds = 604800f; private bool _updatingConfig; private static string PluginDirectory { get { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (!string.IsNullOrEmpty(directoryName)) { return directoryName; } return "."; } } private void Awake() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) BindConfig(); RefreshConfigValues(); new Harmony("com.codex.outward.runicbladelongerduration").PatchAll(typeof(RunicBladeLongerDurationPlugin).Assembly); SL_Item.AddOnInstanceStartListener(2000100, (Action)OnRunicBladeInstanceStart); PatchRunicBladeEffects(); Debug.Log((object)("[Runic Blade Longer Duration] Duration set to " + _durationSeconds.ToString("0", CultureInfo.InvariantCulture) + " seconds. Runic Blade counts as Lexicon: " + s_runicBladeCountsAsLexicon + ". Require Arcane Syntax for Runic Blade Lexicon: " + s_requireArcaneSyntaxForRunicBladeLexicon + ".")); } private void BindConfig() { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Expected O, but got Unknown LegacyConfig legacyConfig = LoadLegacyConfig(); _durationSecondsConfig = ((BaseUnityPlugin)this).Config.Bind("General", "DurationSeconds", legacyConfig.HasDurationSeconds ? legacyConfig.DurationSeconds : 604800f, new ConfigDescription("Duration for Runic Blade and Great Runic Blade summons, in seconds. The mod clamps this to at least the vanilla 180 seconds.", (AcceptableValueBase)(object)new AcceptableValueRange(180f, 315360000f), new object[1] { new ConfigurationManagerAttributes { DispName = "Summon duration, seconds", Order = 30 } })); _runicBladeCountsAsLexiconConfig = ((BaseUnityPlugin)this).Config.Bind("Runic Blade Lexicon", "RunicBladeCountsAsLexicon", !legacyConfig.HasRunicBladeCountsAsLexicon || legacyConfig.RunicBladeCountsAsLexicon, new ConfigDescription("If true, one-handed Runic Blade can count as a Lexicon for rune casting. If false, the mod only changes summon duration.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { DispName = "Runic Blade counts as Lexicon", Order = 20 } })); _requireArcaneSyntaxForRunicBladeLexiconConfig = ((BaseUnityPlugin)this).Config.Bind("Runic Blade Lexicon", "RequireArcaneSyntaxForRunicBladeLexicon", legacyConfig.HasRequireArcaneSyntaxForRunicBladeLexicon && legacyConfig.RequireArcaneSyntaxForRunicBladeLexicon, new ConfigDescription("If true, one-handed Runic Blade counts as a Lexicon only when the owner knows Arcane Syntax. This setting is ignored while RunicBladeCountsAsLexicon is false.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { CustomDrawer = DrawRequireArcaneSyntaxConfig, DispName = "Require Arcane Syntax", Order = 10 } })); _durationSecondsConfig.SettingChanged += OnConfigSettingChanged; _runicBladeCountsAsLexiconConfig.SettingChanged += OnConfigSettingChanged; _requireArcaneSyntaxForRunicBladeLexiconConfig.SettingChanged += OnConfigSettingChanged; ((BaseUnityPlugin)this).Config.Save(); } private void OnConfigSettingChanged(object sender, EventArgs e) { if (!_updatingConfig) { RefreshConfigValues(saveConfig: true); _extendedSummonUids.Clear(); PatchRunicBladeEffects(); ExtendAlreadySummonedRunicWeapons(); Debug.Log((object)("[Runic Blade Longer Duration] Config changed. Duration set to " + _durationSeconds.ToString("0", CultureInfo.InvariantCulture) + " seconds. Runic Blade counts as Lexicon: " + s_runicBladeCountsAsLexicon + ". Require Arcane Syntax for Runic Blade Lexicon: " + s_requireArcaneSyntaxForRunicBladeLexicon + ".")); } } private void RefreshConfigValues(bool saveConfig = false) { _durationSeconds = Math.Max(180f, _durationSecondsConfig.Value); s_durationSeconds = _durationSeconds; s_runicBladeCountsAsLexicon = _runicBladeCountsAsLexiconConfig.Value; if (!s_runicBladeCountsAsLexicon && _requireArcaneSyntaxForRunicBladeLexiconConfig.Value) { _updatingConfig = true; try { _requireArcaneSyntaxForRunicBladeLexiconConfig.Value = false; } finally { _updatingConfig = false; } if (saveConfig) { ((BaseUnityPlugin)this).Config.Save(); } } s_requireArcaneSyntaxForRunicBladeLexicon = s_runicBladeCountsAsLexicon && _requireArcaneSyntaxForRunicBladeLexiconConfig.Value; } private void DrawRequireArcaneSyntaxConfig(ConfigEntryBase entry) { bool flag = _runicBladeCountsAsLexiconConfig == null || _runicBladeCountsAsLexiconConfig.Value; bool flag2 = entry.BoxedValue is bool && (bool)entry.BoxedValue; if (!flag && flag2) { entry.BoxedValue = false; flag2 = false; } bool enabled = GUI.enabled; GUI.enabled = enabled && flag; bool flag3 = GUILayout.Toggle(flag2, GUIContent.none, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }); GUI.enabled = enabled; if (flag && flag3 != flag2) { entry.BoxedValue = flag3; } } private static LegacyConfig LoadLegacyConfig() { string path = Path.Combine(PluginDirectory, "RunicBladeLongerDuration.cfg"); LegacyConfig legacyConfig = new LegacyConfig(); if (!File.Exists(path)) { return legacyConfig; } try { string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0 || text.StartsWith("#", StringComparison.Ordinal)) { continue; } int num = text.IndexOf('='); if (num < 0) { continue; } string text2 = text.Substring(0, num).Trim(); string text3 = text.Substring(num + 1).Trim(); bool result3; if (text2.Equals("DurationSeconds", StringComparison.OrdinalIgnoreCase)) { if (float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { legacyConfig.DurationSeconds = Math.Max(180f, result); legacyConfig.HasDurationSeconds = true; } } else if (text2.Equals("RunicBladeCountsAsLexicon", StringComparison.OrdinalIgnoreCase)) { if (TryParseBool(text3, out var result2)) { legacyConfig.RunicBladeCountsAsLexicon = result2; legacyConfig.HasRunicBladeCountsAsLexicon = true; } } else if (text2.Equals("RequireArcaneSyntaxForRunicBladeLexicon", StringComparison.OrdinalIgnoreCase) && TryParseBool(text3, out result3)) { legacyConfig.RequireArcaneSyntaxForRunicBladeLexicon = result3; legacyConfig.HasRequireArcaneSyntaxForRunicBladeLexicon = true; } } } catch (Exception ex) { Debug.LogWarning((object)("[Runic Blade Longer Duration] Could not read legacy config: " + ex.Message)); } return legacyConfig; } private static bool TryParseBool(string value, out bool result) { if (bool.TryParse(value, out result)) { return true; } if (string.Equals(value, "1", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "yes", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "y", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "on", StringComparison.OrdinalIgnoreCase)) { result = true; return true; } if (string.Equals(value, "0", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "no", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "n", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "off", StringComparison.OrdinalIgnoreCase)) { result = false; return true; } result = false; return false; } private void PatchRunicBladeEffects() { RunicBlade[] array = Resources.FindObjectsOfTypeAll(); foreach (RunicBlade val in array) { if (!((Object)(object)val == (Object)null) && !(Math.Abs(val.SummonLifeSpan - _durationSeconds) < 0.01f)) { val.SummonLifeSpan = _durationSeconds; } } } private void ExtendAlreadySummonedRunicWeapons() { SummonedEquipment[] array = Resources.FindObjectsOfTypeAll(); foreach (SummonedEquipment val in array) { if ((Object)(object)val == (Object)null || val.RemainingLifespan <= 0f) { continue; } Item component = ((Component)val).GetComponent(); if (!((Object)(object)component == (Object)null) && (component.ItemID == 2000100 || component.ItemID == 2100999)) { string uID = component.UID; if (!string.IsNullOrEmpty(uID) && _extendedSummonUids.Add(uID) && val.RemainingLifespan < _durationSeconds) { val.RefreshLifespan(_durationSeconds); } } } } private void OnRunicBladeInstanceStart(Item item) { if (!((Object)(object)item == (Object)null) && s_runicBladeCountsAsLexicon && !s_requireArcaneSyntaxForRunicBladeLexicon) { CustomTags.SetTagSource(((Component)item).gameObject, LexiconTag, false); } } private static bool IsRunicBlade(Item item) { if ((Object)(object)item != (Object)null) { return item.ItemID == 2000100; } return false; } private static bool IsRunicWeapon(Item item) { if ((Object)(object)item != (Object)null) { if (item.ItemID != 2000100) { return item.ItemID == 2100999; } return true; } return false; } private static bool IsRunicSummonedEquipment(SummonedEquipment summoned) { return IsRunicWeapon(((Object)(object)summoned == (Object)null) ? null : ((Component)summoned).GetComponent()); } private static bool IsLexiconTag(Tag tag) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return string.Equals(tag.TagName, "Lexicon", StringComparison.Ordinal); } private static bool RunicBladeLexiconAllowed(Item item) { if (!s_runicBladeCountsAsLexicon) { return false; } if (!s_requireArcaneSyntaxForRunicBladeLexicon) { return true; } Character val = (((Object)(object)item == (Object)null) ? null : ((EffectSynchronizer)item).OwnerCharacter); if ((Object)(object)val != (Object)null && (Object)(object)val.Inventory != (Object)null && (Object)(object)val.Inventory.SkillKnowledge != (Object)null) { return ((CharacterKnowledge)val.Inventory.SkillKnowledge).IsItemLearned(8205180); } return false; } }