Decompiled source of Deathlink v0.7.5
plugins/Deathlink.dll
Decompiled 2 weeks 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.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Versioning; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using AlmanacClasses.API; using AzuExtendedPlayerInventory; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Deathlink.Common; using Deathlink.Death; using HarmonyLib; using JetBrains.Annotations; using Jotunn; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using SimpleJson; using Splatform; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ValRougelike")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ValRougelike")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("0.0.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.1.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace AlmanacClasses.API { public static class ClassesAPI { private static readonly MethodInfo? API_AddExperience; private static readonly MethodInfo? API_GetLevel; private static readonly MethodInfo? API_GetCharacteristic; public static void AddEXP(int amount) { API_AddExperience?.Invoke(null, new object[1] { amount }); } public static int GetLevel() { return (int)(API_GetLevel?.Invoke(null, null) ?? ((object)0)); } public static int GetCharacteristic(string type) { return (int)(API_GetCharacteristic?.Invoke(null, new object[1] { type }) ?? ((object)0)); } public static int GetConstitution() { return GetCharacteristic("Constitution"); } public static int GetDexterity() { return GetCharacteristic("Dexterity"); } public static int GetStrength() { return GetCharacteristic("Strength"); } public static int GetIntelligence() { return GetCharacteristic("Intelligence"); } public static int GetWisdom() { return GetCharacteristic("Wisdom"); } static ClassesAPI() { Type type = Type.GetType("AlmanacClasses.API.API, AlmanacClasses"); if ((object)type != null) { API_AddExperience = type.GetMethod("AddExperience", BindingFlags.Static | BindingFlags.Public); API_GetLevel = type.GetMethod("GetLevel", BindingFlags.Static | BindingFlags.Public); API_GetCharacteristic = type.GetMethod("GetCharacteristic", BindingFlags.Static | BindingFlags.Public); } } } } namespace Deathlink { internal class Logger { public static LogLevel Level = (LogLevel)16; public static void enableDebugLogging(object sender, EventArgs e) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (ValConfig.EnableDebugMode.Value) { Level = (LogLevel)32; } else { Level = (LogLevel)16; } } public static void CheckEnableDebugLogging() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (ValConfig.EnableDebugMode.Value) { Level = (LogLevel)32; } else { Level = (LogLevel)16; } } public static void LogDebug(string message) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)Level >= 32) { Deathlink.Log.LogInfo((object)message); } } public static void LogInfo(string message) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)Level >= 16) { Deathlink.Log.LogInfo((object)message); } } public static void LogWarning(string message) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 if ((int)Level >= 4) { Deathlink.Log.LogWarning((object)message); } } public static void LogError(string message) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 if ((int)Level >= 2) { Deathlink.Log.LogError((object)message); } } } public static class Extensions { private static readonly List<ItemType> EquipmentTypes = new List<ItemType> { (ItemType)7, (ItemType)12, (ItemType)6, (ItemType)11, (ItemType)4, (ItemType)5, (ItemType)19, (ItemType)17, (ItemType)18, (ItemType)3, (ItemType)14, (ItemType)22, (ItemType)20 }; public static List<ItemData> GetEquipment(this List<ItemData> list) { return list.Where((ItemData x) => EquipmentTypes.Contains(x.m_shared.m_itemType)).ToList(); } public static List<ItemData> GetNotEquipment(this List<ItemData> list) { return list.Where((ItemData x) => !EquipmentTypes.Contains(x.m_shared.m_itemType)).ToList(); } public static bool IsEquipment(this ItemData item) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (EquipmentTypes.Contains(item.m_shared.m_itemType)) { return true; } return false; } } [BepInPlugin("MidnightsFX.Deathlink", "Deathlink", "0.7.5")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] internal class Deathlink : BaseUnityPlugin { public const string PluginGUID = "MidnightsFX.Deathlink"; public const string PluginName = "Deathlink"; public const string PluginVersion = "0.7.5"; public ValConfig cfg; internal static AssetBundle EmbeddedResourceBundle; internal static bool AzuEPILoaded = false; internal static bool RustyAlmanacClassesLoaded = false; public static CustomLocalization Localization = LocalizationManager.Instance.GetLocalization(); public static ManualLogSource Log; public void Awake() { Log = ((BaseUnityPlugin)this).Logger; cfg = new ValConfig(((BaseUnityPlugin)this).Config); AddLocalizations(); EmbeddedResourceBundle = AssetUtils.LoadAssetBundleFromResources("Deathlink.AssetsEmbedded.deathless", typeof(Deathlink).Assembly); DeathProgressionSkill.SetupDeathSkill(); if (API.IsLoaded()) { AzuEPILoaded = true; } if (BepInExUtils.GetPlugins(false).Keys.Contains("RustyMods.AlmanacClasses")) { RustyAlmanacClassesLoaded = true; } Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null); DeathConfigurationData.Init(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Death is not the end."); } public static DataObjects.DeathChoiceLevel pcfg() { return DeathConfigurationData.playerDeathConfiguration; } private void AddLocalizations() { Localization = LocalizationManager.Instance.GetLocalization(); string text = Path.Combine(Paths.ConfigPath, "Deathlink", "localizations"); Directory.CreateDirectory(text); string[] manifestResourceNames = typeof(Deathlink).Assembly.GetManifestResourceNames(); foreach (string text2 in manifestResourceNames) { if (!text2.Contains("Localizations")) { continue; } string text3 = Regex.Replace(ReadEmbeddedResourceFile(text2), "\\/\\/.*", ""); Dictionary<string, string> internal_localization = SimpleJson.DeserializeObject<Dictionary<string, string>>(text3); string[] array = text2.Split(new char[1] { '.' }); if (File.Exists(text + "/" + array[2] + ".json")) { string text4 = File.ReadAllText(text + "/" + array[2] + ".json"); try { Dictionary<string, string> dictionary = SimpleJson.DeserializeObject<Dictionary<string, string>>(text4); UpdateLocalizationWithMissingKeys(internal_localization, dictionary); ((BaseUnityPlugin)this).Logger.LogDebug((object)("Reading " + text + "/" + array[2] + ".json")); File.WriteAllText(text + "/" + array[2] + ".json", SimpleJson.SerializeObject((object)dictionary)); string text5 = File.ReadAllText(text + "/" + array[2] + ".json"); Localization.AddJsonFile(array[2], text5); } catch { File.WriteAllText(text + "/" + array[2] + ".json", text3); ((BaseUnityPlugin)this).Logger.LogDebug((object)("Reading " + text2)); Localization.AddJsonFile(array[2], text3); } } else { File.WriteAllText(text + "/" + array[2] + ".json", text3); ((BaseUnityPlugin)this).Logger.LogDebug((object)("Reading " + text2)); Localization.AddJsonFile(array[2], text3); } ((BaseUnityPlugin)this).Logger.LogDebug((object)("Added localization: '" + array[2] + "'")); } } private void UpdateLocalizationWithMissingKeys(Dictionary<string, string> internal_localization, Dictionary<string, string> cached_localization) { if (internal_localization.Keys.Count == cached_localization.Keys.Count) { return; } ((BaseUnityPlugin)this).Logger.LogDebug((object)"Cached localization was missing some entries. They will be added."); foreach (KeyValuePair<string, string> item in internal_localization) { if (!cached_localization.ContainsKey(item.Key)) { cached_localization.Add(item.Key, item.Value); } } } private string ReadEmbeddedResourceFile(string filename) { using Stream stream = typeof(Deathlink).Assembly.GetManifestResourceStream(filename); using StreamReader streamReader = new StreamReader(stream); return streamReader.ReadToEnd(); } public static List<T> shuffleList<T>(List<T> inputList) { int i = 0; int count = inputList.Count; int num = 0; T val = default(T); List<T> list = new List<T>(); list.AddRange(inputList); for (; i < count; i++) { num = Random.Range(i, list.Count); val = list[i]; list[i] = list[num]; list[num] = val; } return list; } } } namespace Deathlink.Death { public static class Compendium { [HarmonyPatch(typeof(TextsDialog), "UpdateTextsList")] public static class TextsDialog_UpdateTextsList_Patch { public static void Postfix(TextsDialog __instance) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { AddDeathLinkExplanationPage(__instance, localPlayer); } } private static void AddDeathLinkExplanationPage(TextsDialog textsDialog, Player player) { //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Expected O, but got Unknown long playerID = Player.m_localPlayer.GetPlayerID(); if (DeathConfigurationData.playerSettings.ContainsKey(playerID)) { string deathChoiceLevel = DeathConfigurationData.playerSettings[playerID].DeathChoiceLevel; DataObjects.DeathChoiceLevel deathChoiceLevel2 = DeathConfigurationData.DeathLevels[deathChoiceLevel]; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("<size=48>" + Localization.instance.Localize("$comp_header") + ": <color=" + specialColor + ">" + deathChoiceLevel2.DisplayName + "</color></size>"); stringBuilder.AppendLine(); stringBuilder.AppendLine("<size=30><b>Death Effects</b></size>"); stringBuilder.AppendLine(deathChoiceLevel2.GetDeathStyleDescription()); stringBuilder.AppendLine(); if (deathChoiceLevel2.SkillModifiers.Count > 0) { stringBuilder.AppendLine("<size=30><b>Skill Modifiers</b></size>"); stringBuilder.AppendLine(deathChoiceLevel2.GetSkillModiferDescription()); stringBuilder.AppendLine(); } if (deathChoiceLevel2.ResourceModifiers.Count > 0) { stringBuilder.AppendLine("<size=30><b>Resource Modifiers</b></size>"); stringBuilder.AppendLine(deathChoiceLevel2.GetResourceModiferDescription()); stringBuilder.AppendLine(); } if (deathChoiceLevel2.DeathLootModifiers.Count > 0) { stringBuilder.AppendLine("<size=30><b>Loot Modifiers</b></size>"); stringBuilder.AppendLine(deathChoiceLevel2.GetLootModifiersDescription()); stringBuilder.AppendLine(); } textsDialog.m_texts.Insert(0, new TextInfo(Localization.instance.Localize("$deathlink_settings"), Localization.instance.Localize(stringBuilder.ToString()))); } } } private static string specialColor = "#ffa64d"; } public static class DeathChoiceEnable { [HarmonyPatch(typeof(InventoryGui), "Show")] public static class ShowDeathChoiceUI { public static void Postfix(InventoryGui __instance) { if (!((Object)(object)Player.m_localPlayer != (Object)null) || !DeathConfigurationData.playerSettings.ContainsKey(Player.m_localPlayer.GetPlayerID())) { if ((Object)(object)((Component)__instance).gameObject.GetComponent<DeathChoices.DeathChoiceUI>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<DeathChoices.DeathChoiceUI>(); } DeathChoices.DeathChoiceUI.Instance.Show(); } } } [HarmonyPatch(typeof(InventoryGui), "Hide")] public static class HideDeathChoiceUI { public static void Postfix(InventoryGui __instance) { DeathChoices.DeathChoiceUI.Instance.Hide(); } } } public class DeathChoices { public class DeathChoiceUI : MonoBehaviour { private static DeathChoiceUI _instance; private static GameObject DeathChoicePanel; private static GameObject ChoicesScrollView; private static GameObject ChoicesContent; private static GameObject ChoicesContainer; private static GameObject manualCloseButton; private static GameObject selectChoiceButton; private static Text DeathPenaltyDescription; private static Text XPModifiersDescription; private static Text LootModifersDescription; private static Text HarvestModifiersDescription; private static List<Toggle> difficultyToggles = new List<Toggle>(); private static ToggleGroup choiceGroup; private static string selectedDeathChoice = "none"; public static DeathChoiceUI Instance => _instance ?? (_instance = new DeathChoiceUI()); public void Awake() { CreateStaticUIObjects(); SetChoiceList(); } public void Show() { if ((Object)(object)DeathChoicePanel == (Object)null) { CreateStaticUIObjects(); } DeathChoicePanel.SetActive(true); } public void Hide() { GameObject deathChoicePanel = DeathChoicePanel; if (deathChoicePanel != null) { deathChoicePanel.SetActive(false); } GUIManager.BlockInput(false); } public void MakePlayerDeathSelection() { if ((Object)(object)Player.m_localPlayer == (Object)null) { Logger.LogWarning("Player not set, ensure the local player is set."); return; } if (selectedDeathChoice == "none") { Logger.LogWarning("No death type selected"); return; } DeathConfigurationData.playerSettings.Add(Player.m_localPlayer.GetPlayerID(), new DataObjects.DeathConfiguration { DeathChoiceLevel = selectedDeathChoice }); DeathConfigurationData.CheckAndSetPlayerDeathConfig(); DeathConfigurationData.WritePlayerChoices(); Hide(); } private void SetChoiceList() { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) difficultyToggles.Clear(); int num = -50; foreach (KeyValuePair<string, DataObjects.DeathChoiceLevel> entry in DeathConfigurationData.DeathLevels) { GameObject obj = Object.Instantiate<GameObject>(ChoicesContainer, ChoicesContent.transform); Transform val = obj.transform.Find("selecter"); ((Component)val.Find("Label")).GetComponent<Text>().text = entry.Key; ((Component)obj.transform.Find("ChoiceName")).GetComponent<Text>().text = entry.Value.DisplayName; Toggle component = ((Component)val).GetComponent<Toggle>(); component.group = choiceGroup; ((UnityEvent<bool>)(object)component.onValueChanged).AddListener((UnityAction<bool>)delegate { ((Component)DeathPenaltyDescription).GetComponent<Text>().text = entry.Value.GetDeathStyleDescription(); ((Component)XPModifiersDescription).GetComponent<Text>().text = entry.Value.GetSkillModiferDescription(); ((Component)LootModifersDescription).GetComponent<Text>().text = entry.Value.GetLootModifiersDescription(); ((Component)HarvestModifiersDescription).GetComponent<Text>().text = entry.Value.GetResourceModiferDescription(); selectedDeathChoice = entry.Key; }); obj.SetActive(true); obj.transform.localPosition = new Vector3 { x = 260f, y = num }; difficultyToggles.Add(component); num -= 50; } } private void CreateStaticUIObjects() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: 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_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0141: 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) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01be: 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_0203: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Expected O, but got Unknown //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Unknown result type (might be due to invalid IL or missing references) //IL_035f: Unknown result type (might be due to invalid IL or missing references) //IL_036e: Unknown result type (might be due to invalid IL or missing references) //IL_037d: Unknown result type (might be due to invalid IL or missing references) //IL_0393: Unknown result type (might be due to invalid IL or missing references) //IL_0399: Unknown result type (might be due to invalid IL or missing references) //IL_03e0: Unknown result type (might be due to invalid IL or missing references) //IL_03ef: Unknown result type (might be due to invalid IL or missing references) //IL_03fe: Unknown result type (might be due to invalid IL or missing references) //IL_040f: Unknown result type (might be due to invalid IL or missing references) //IL_0415: Unknown result type (might be due to invalid IL or missing references) //IL_047e: Unknown result type (might be due to invalid IL or missing references) //IL_048d: Unknown result type (might be due to invalid IL or missing references) //IL_049c: Unknown result type (might be due to invalid IL or missing references) //IL_04b2: Unknown result type (might be due to invalid IL or missing references) //IL_04b8: Unknown result type (might be due to invalid IL or missing references) //IL_04ff: Unknown result type (might be due to invalid IL or missing references) //IL_050e: Unknown result type (might be due to invalid IL or missing references) //IL_051d: Unknown result type (might be due to invalid IL or missing references) //IL_052e: Unknown result type (might be due to invalid IL or missing references) //IL_0534: Unknown result type (might be due to invalid IL or missing references) //IL_059d: Unknown result type (might be due to invalid IL or missing references) //IL_05ac: Unknown result type (might be due to invalid IL or missing references) //IL_05bb: Unknown result type (might be due to invalid IL or missing references) //IL_05d1: Unknown result type (might be due to invalid IL or missing references) //IL_05d7: Unknown result type (might be due to invalid IL or missing references) //IL_061e: Unknown result type (might be due to invalid IL or missing references) //IL_062d: Unknown result type (might be due to invalid IL or missing references) //IL_063c: Unknown result type (might be due to invalid IL or missing references) //IL_064d: Unknown result type (might be due to invalid IL or missing references) //IL_0653: Unknown result type (might be due to invalid IL or missing references) //IL_06bc: Unknown result type (might be due to invalid IL or missing references) //IL_06cb: Unknown result type (might be due to invalid IL or missing references) //IL_06da: Unknown result type (might be due to invalid IL or missing references) //IL_0710: Unknown result type (might be due to invalid IL or missing references) //IL_071a: Expected O, but got Unknown //IL_0744: Unknown result type (might be due to invalid IL or missing references) //IL_0749: Unknown result type (might be due to invalid IL or missing references) //IL_076e: Unknown result type (might be due to invalid IL or missing references) //IL_078c: Unknown result type (might be due to invalid IL or missing references) //IL_078d: Unknown result type (might be due to invalid IL or missing references) //IL_07c9: Unknown result type (might be due to invalid IL or missing references) //IL_07d3: Expected O, but got Unknown //IL_0800: Unknown result type (might be due to invalid IL or missing references) //IL_0843: Unknown result type (might be due to invalid IL or missing references) //IL_0848: Unknown result type (might be due to invalid IL or missing references) //IL_08a1: Unknown result type (might be due to invalid IL or missing references) //IL_08b0: Unknown result type (might be due to invalid IL or missing references) //IL_08bf: Unknown result type (might be due to invalid IL or missing references) //IL_08d5: Unknown result type (might be due to invalid IL or missing references) //IL_08db: Unknown result type (might be due to invalid IL or missing references) if (GUIManager.Instance == null || !Object.op_Implicit((Object)(object)GUIManager.CustomGUIFront)) { Logger.LogWarning("GUIManager not setup, skipping static object creation."); return; } DeathChoicePanel = GUIManager.Instance.CreateWoodpanel(GUIManager.CustomGUIFront.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 0f), 800f, 800f, true); DeathChoicePanel.SetActive(false); ((Object)GUIManager.Instance.CreateText(Localization.instance.Localize("$selection_header"), DeathChoicePanel.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(50f, 360f), GUIManager.Instance.AveriaSerifBold, 30, GUIManager.Instance.ValheimOrange, true, Color.black, 350f, 40f, false)).name = "DLHeader"; Text component = GUIManager.Instance.CreateText(Localization.instance.Localize("$selection_description"), DeathChoicePanel.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 315f), GUIManager.Instance.AveriaSerif, 20, Color.white, true, Color.black, 560f, 60f, false).GetComponent<Text>(); component.resizeTextForBestFit = true; component.resizeTextMaxSize = 20; component.alignment = (TextAnchor)4; manualCloseButton = GUIManager.Instance.CreateButton(Localization.instance.Localize("$close"), DeathChoicePanel.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(360f, 360f), 60f, 60f); Button component2 = manualCloseButton.GetComponent<Button>(); ((Selectable)component2).interactable = true; ((UnityEvent)component2.onClick).AddListener(new UnityAction(Hide)); manualCloseButton.SetActive(false); ((Object)GUIManager.Instance.CreateText(Localization.instance.Localize("$death_penalty_header"), DeathChoicePanel.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(100f, 220f), GUIManager.Instance.AveriaSerifBold, 22, GUIManager.Instance.ValheimOrange, true, Color.black, 400f, 40f, false)).name = "DeathPenaltyTitle"; GameObject obj = GUIManager.Instance.CreateText(Localization.instance.Localize("$death_penalty_description"), DeathChoicePanel.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(150f, 110f), GUIManager.Instance.AveriaSerifBold, 18, Color.white, true, Color.black, 500f, 200f, false); ((Object)obj).name = "DeathPenaltyDesc"; DeathPenaltyDescription = obj.GetComponent<Text>(); DeathPenaltyDescription.resizeTextForBestFit = true; DeathPenaltyDescription.resizeTextMaxSize = 18; ((Object)GUIManager.Instance.CreateText(Localization.instance.Localize("$xp_header"), DeathChoicePanel.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(100f, 30f), GUIManager.Instance.AveriaSerifBold, 22, GUIManager.Instance.ValheimOrange, true, Color.black, 400f, 40f, false)).name = "xpModifiersTitle"; GameObject obj2 = GUIManager.Instance.CreateText(Localization.instance.Localize("$xp_mod_description"), DeathChoicePanel.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(150f, -80f), GUIManager.Instance.AveriaSerifBold, 16, Color.white, true, Color.black, 500f, 200f, false); ((Object)obj2).name = "xpModifiersDesc"; XPModifiersDescription = obj2.GetComponent<Text>(); XPModifiersDescription.resizeTextForBestFit = true; XPModifiersDescription.resizeTextMaxSize = 18; ((Object)GUIManager.Instance.CreateText(Localization.instance.Localize("$loot_header"), DeathChoicePanel.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(100f, -130f), GUIManager.Instance.AveriaSerifBold, 22, GUIManager.Instance.ValheimOrange, true, Color.black, 400f, 40f, false)).name = "lootModifiersTitle"; GameObject obj3 = GUIManager.Instance.CreateText(Localization.instance.Localize("$loot_modifier_description"), DeathChoicePanel.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(150f, -240f), GUIManager.Instance.AveriaSerifBold, 16, Color.white, true, Color.black, 500f, 200f, false); ((Object)obj3).name = "lootModifersDesc"; LootModifersDescription = obj3.GetComponent<Text>(); LootModifersDescription.resizeTextForBestFit = true; LootModifersDescription.resizeTextMaxSize = 18; ((Object)GUIManager.Instance.CreateText(Localization.instance.Localize("$harvest_header"), DeathChoicePanel.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(100f, -260f), GUIManager.Instance.AveriaSerifBold, 22, GUIManager.Instance.ValheimOrange, true, Color.black, 400f, 40f, false)).name = "harvestModifiersTitle"; GameObject obj4 = GUIManager.Instance.CreateText(Localization.instance.Localize("$harvest_modifier_description"), DeathChoicePanel.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(150f, -370f), GUIManager.Instance.AveriaSerifBold, 16, Color.white, true, Color.black, 500f, 200f, false); ((Object)obj4).name = "harvestModifersDesc"; HarvestModifiersDescription = obj4.GetComponent<Text>(); HarvestModifiersDescription.resizeTextForBestFit = true; HarvestModifiersDescription.resizeTextMaxSize = 18; selectChoiceButton = GUIManager.Instance.CreateButton(Localization.instance.Localize("$deathchoice_select"), DeathChoicePanel.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(-240f, -290f), 200f, 80f); Button component3 = selectChoiceButton.GetComponent<Button>(); ((Selectable)component3).interactable = true; ((UnityEvent)component3.onClick).AddListener(new UnityAction(MakePlayerDeathSelection)); Logger.LogDebug("Setting up scroll entry"); ChoicesScrollView = GUIManager.Instance.CreateScrollView(DeathChoicePanel.transform, false, true, 10f, 10f, GUIManager.Instance.ValheimScrollbarHandleColorBlock, Color.grey, 200f, 400f); ChoicesScrollView.transform.localPosition = Vector2.op_Implicit(new Vector2 { x = -260f, y = -30f }); ChoicesContent = ((Component)ChoicesScrollView.GetComponentInChildren<ContentSizeFitter>()).gameObject; choiceGroup = ChoicesContent.AddComponent<ToggleGroup>(); Logger.LogDebug("Setting up death choice template entry"); ChoicesContainer = new GameObject("DeathChoice"); ChoicesContainer.transform.SetParent(DeathChoicePanel.transform); ChoicesContainer.transform.position = DeathChoicePanel.transform.position; ChoicesContainer.SetActive(false); GameObject obj5 = GUIManager.Instance.CreateToggle(ChoicesContainer.transform, 40f, 40f); obj5.transform.localPosition = Vector2.op_Implicit(new Vector2(-220f, 0f)); ((Object)obj5).name = "selecter"; ((Component)obj5.transform.Find("Label")).gameObject.SetActive(false); obj5.GetComponent<Toggle>().isOn = false; ((Object)GUIManager.Instance.CreateText("Name", ChoicesContainer.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(-20f, 0f), GUIManager.Instance.AveriaSerifBold, 20, GUIManager.Instance.ValheimYellow, true, Color.black, 350f, 40f, false)).name = "ChoiceName"; } } } public static class DeathProgressionSkill { [HarmonyPatch(typeof(Player), "RaiseSkill")] public class Deathskill_EXP_Patch { public static void Postfix(Player __instance) { //IL_037a: Unknown result type (might be due to invalid IL or missing references) timeSinceGameStart += Time.deltaTime; if (lastSkillIncreaseTickTime == 0f) { lastSkillIncreaseTickTime = timeSinceGameStart + ValConfig.SkillProgressUpdateCheckInterval.Value; PlayerProfile playerProfile = Game.instance.GetPlayerProfile(); _bossKills = playerProfile.m_playerStats.m_stats[(PlayerStatType)85]; _enemykills = playerProfile.m_playerStats.m_stats[(PlayerStatType)6]; _piecesBuilt = playerProfile.m_playerStats.m_stats[(PlayerStatType)2]; _treesChopped = playerProfile.m_playerStats.m_stats[(PlayerStatType)27]; _mineAmount = playerProfile.m_playerStats.m_stats[(PlayerStatType)38]; _craftAndUpgrades = playerProfile.m_playerStats.m_stats[(PlayerStatType)1]; } if (timeSinceGameStart > lastSkillIncreaseTickTime) { lastSkillIncreaseTickTime = timeSinceGameStart + ValConfig.SkillProgressUpdateCheckInterval.Value; PlayerProfile playerProfile2 = Game.instance.GetPlayerProfile(); float num = playerProfile2.m_playerStats.m_stats[(PlayerStatType)85]; float num2 = playerProfile2.m_playerStats.m_stats[(PlayerStatType)6]; float num3 = playerProfile2.m_playerStats.m_stats[(PlayerStatType)2]; float num4 = playerProfile2.m_playerStats.m_stats[(PlayerStatType)27]; float num5 = playerProfile2.m_playerStats.m_stats[(PlayerStatType)38]; float num6 = playerProfile2.m_playerStats.m_stats[(PlayerStatType)1]; float num7 = 0f; float num8 = 0f; float num9 = 0f; float num10 = 0f; float num11 = 0f; float num12 = 0f; if (num > _bossKills || num2 > _enemykills) { num12 = (num - _bossKills) * ValConfig.SkillGainOnBossKills.Value; num11 = (num2 - _enemykills) * ValConfig.SkillGainOnKills.Value; Logger.LogDebug($"DeathProgression kill skill bosskill: {num12} kill: {num11}"); _bossKills = num; _enemykills = num2; } if (num3 > _piecesBuilt) { num10 = (num3 - _piecesBuilt) * ValConfig.SkillGainOnBuilding.Value; Logger.LogDebug($"DeathProgression building skill: {num10}"); _piecesBuilt = num3; } if (num4 > _treesChopped || num5 > _mineAmount) { num9 = (num4 - _treesChopped) * ValConfig.SkillGainOnResourceGathering.Value; num8 = (num5 - _mineAmount) * ValConfig.SkillGainOnResourceGathering.Value; Logger.LogDebug($"DeathProgression harvesting skill tree_harvest: {num9} mining: {num8}"); _treesChopped = num4; _mineAmount = num5; } if (num6 > _craftAndUpgrades) { num7 = (num6 - _craftAndUpgrades) * ValConfig.SkillGainOnCrafts.Value; Logger.LogDebug($"DeathProgression crafting skill crafting: {num7}"); _craftAndUpgrades = num6; } float num13 = num12 + num11 + num10 + num10 + num9 + num8 + num7; float num14 = (float)Math.Log(__instance.m_timeSinceDeath) / 5f * 0.5f; float num15 = num14 * num13; if (Deathlink.RustyAlmanacClassesLoaded) { int num16 = Mathf.RoundToInt(num15 * ValConfig.AlmanacClassesXPGainScale.Value); Logger.LogDebug($"Almanac XP Gain {num16}"); ClassesAPI.AddEXP(num16); } Logger.LogDebug($"DeathProgression skill bonus from survival (survive time: {__instance.m_timeSinceDeath}) {num14} x {num13} = {num15}"); ((Character)Player.m_localPlayer).RaiseSkill(DeathSkill, num15); } } } public static SkillType DeathSkill; private static float lastSkillIncreaseTickTime; private static float timeSinceGameStart; private static float _bossKills; private static float _enemykills; private static float _piecesBuilt; private static float _mineAmount; private static float _treesChopped; private static float _craftAndUpgrades; public static void SetupDeathSkill() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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_0081: Unknown result type (might be due to invalid IL or missing references) SkillConfig val = new SkillConfig(); val.Name = LocalizationManager.Instance.TryTranslate("$death_skill"); val.Description = LocalizationManager.Instance.TryTranslate("$death_skill_description"); val.Icon = Deathlink.EmbeddedResourceBundle.LoadAsset<Sprite>("Assets/Custom/Icons/death_skill.png"); val.Identifier = "midnightsfx.deathskill"; val.IncreaseStep = 0.1f; DeathSkill = SkillManager.Instance.AddSkill(val); if (!SkillsChanges.skills_to_avoid_standard_death_penalty.Contains(DeathSkill)) { SkillsChanges.skills_to_avoid_standard_death_penalty.Add(DeathSkill); } } public static float DeathSkillCalculatePercentWithBonus(float bonus = 0f, float min = 0.01f, float max = 1f) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) float num = 0f; if ((Object)(object)Player.m_localPlayer != (Object)null) { float skillFactor = ((Character)Player.m_localPlayer).GetSkillFactor(DeathSkill); num += skillFactor; } num += bonus; if (num < min) { num = min; } if (num > max) { num = max; } return num; } } public static class HarvestModifiers { [HarmonyPatch(typeof(TreeLog), "Destroy")] public static class IncreaseDropsFromTree { private static void Postfix(TreeLog __instance, HitData hitData) { //IL_002f: 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_0051: Unknown result type (might be due to invalid IL or missing references) if (Deathlink.pcfg().ResourceModifiers != null && Deathlink.pcfg().ResourceModifiers.Count > 0 && hitData != null && (Object)(object)Player.m_localPlayer != (Object)null && hitData.m_attacker == ((Character)Player.m_localPlayer).GetZDOID()) { IncreaseDrops(__instance.m_dropWhenDestroyed, ((Component)__instance).transform.position); } } } [HarmonyPatch(typeof(Pickable), "Drop")] public static class IncreaseDropsPickable { private static void Prefix(Pickable __instance, GameObject prefab, int offset) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: 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_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0102: 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) if (Deathlink.pcfg().ResourceModifiers == null || Deathlink.pcfg().ResourceModifiers.Count <= 0 || !((Object)(object)Player.m_localPlayer != (Object)null)) { return; } float num = Deathlink.pcfg().GetResouceEarlyCache(prefab.gameObject) - 1f; int num2 = 0; while (num > 0f) { float value = Random.value; Logger.LogDebug($"Checking to increase drops {value} <= {num}"); if (value <= num) { num2++; } num -= 1f; } if (num2 > 0) { Vector2 val = Random.insideUnitCircle * 0.2f; Vector3 val2 = ((Component)__instance).transform.position + Vector3.up * __instance.m_spawnOffset + new Vector3(val.x, 0.5f * (float)offset, val.y); Quaternion val3 = Quaternion.Euler(0f, (float)Random.Range(0, 360), 0f); for (int i = 0; i < num2; i++) { Object.Instantiate<GameObject>(prefab, val2, val3); } } } } [HarmonyPatch(typeof(MineRock5), "RPC_SetAreaHealth")] public static class Minerock5DestroyPatch { private static void Postfix(MineRock5 __instance, long sender, int index, float health) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) if (Deathlink.pcfg().ResourceModifiers != null && Deathlink.pcfg().ResourceModifiers.Count > 0 && (Object)(object)Player.m_localPlayer != (Object)null && health <= 0f) { IncreaseDrops(__instance.m_dropItems, ((Component)__instance).gameObject.transform.position); } } } [HarmonyPatch(typeof(Destructible), "Destroy")] public static class IncreaseDropsFromDestructible { private static void Prefix(Destructible __instance, HitData hit) { //IL_002f: 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 (Deathlink.pcfg().ResourceModifiers != null && Deathlink.pcfg().ResourceModifiers.Count > 0 && hit != null && (Object)(object)Player.m_localPlayer != (Object)null && hit.m_attacker == ((Character)Player.m_localPlayer).GetZDOID()) { IncreaseDestructibleDrops(__instance); } } public static void IncreaseDestructibleDrops(Destructible destructible) { //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_003a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)destructible.m_spawnWhenDestroyed != (Object)null)) { Vector3 position = ((Component)destructible).transform.position; DropOnDestroyed component = ((Component)destructible).GetComponent<DropOnDestroyed>(); if (!((Object)(object)component == (Object)null) && component.m_dropWhenDestroyed != null) { IncreaseDrops(component.m_dropWhenDestroyed, position); } } } } public static void IncreaseDrops(DropTable drops, Vector3 position) { //IL_0065: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_0109: 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_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) List<KeyValuePair<GameObject, int>> list = new List<KeyValuePair<GameObject, int>>(); List<KeyValuePair<GameObject, int>> list2 = Deathlink.pcfg().RollHarvestLoot(); if (list2.Count > 0) { list.AddRange(list2); } int num = Random.Range(drops.m_dropMin, drops.m_dropMax); int num2 = drops.m_drops.Count; if (num == 0) { num = 1; } if (num2 == 0) { num2 = 1; } int num3 = Mathf.RoundToInt((float)(num / num2)); foreach (DropData drop in drops.m_drops) { float resouceEarlyCache = Deathlink.pcfg().GetResouceEarlyCache(drop.m_item); if (resouceEarlyCache != 1f) { int num4 = Mathf.RoundToInt((float)num3 * resouceEarlyCache); if (num4 > 0) { list.Add(new KeyValuePair<GameObject, int>(drop.m_item, num4)); } } } if (list.Count <= 0) { return; } Logger.LogDebug("Deathlink drop increase."); foreach (KeyValuePair<GameObject, int> item in list) { Quaternion val = Quaternion.Euler(0f, (float)Random.Range(0, 360), 0f); int maxStackSize = item.Key.GetComponent<ItemDrop>().m_itemData.m_shared.m_maxStackSize; int num5 = item.Value; if (num5 > maxStackSize) { int num6 = num5 / maxStackSize; for (int i = 0; i < num6; i++) { Object.Instantiate<GameObject>(item.Key, position, val).GetComponent<ItemDrop>().m_itemData.m_stack = maxStackSize; Logger.LogDebug($"Dropping {maxStackSize} of {((Object)item.Key).name} to the world."); } num5 -= maxStackSize * num6; } Object.Instantiate<GameObject>(item.Key, position, val).GetComponent<ItemDrop>().m_itemData.m_stack = num5; Logger.LogDebug($"Dropping {num5} of {((Object)item.Key).name} to the world."); } } } public static class LootModifiers { [HarmonyPatch(typeof(CharacterDrop))] public static class CalculateLootByModifiers { [HarmonyPostfix] [HarmonyPatch("GenerateDropList")] private static void Postfix(List<KeyValuePair<GameObject, int>> __result, CharacterDrop __instance) { //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) if (!__instance.m_character.m_localPlayerHasHit || Deathlink.pcfg().DeathLootModifiers == null || Deathlink.pcfg().DeathLootModifiers.Count == 0) { return; } List<KeyValuePair<GameObject, int>> list = new List<KeyValuePair<GameObject, int>>(); foreach (KeyValuePair<GameObject, int> item in __result) { float resouceEarlyCache = Deathlink.pcfg().GetResouceEarlyCache(((Object)item.Key).name); if (resouceEarlyCache == 1f) { list.Add(item); continue; } int num = Mathf.RoundToInt((float)item.Value * resouceEarlyCache); if (num >= 1) { list.Add(new KeyValuePair<GameObject, int>(item.Key, num)); } } List<KeyValuePair<GameObject, int>> list2 = Deathlink.pcfg().RollKillLoot(); if (list2.Count > 0) { foreach (KeyValuePair<GameObject, int> item2 in list2) { for (int i = 0; i < item2.Value; i++) { Object.Instantiate<GameObject>(item2.Key, ((Component)__instance).transform.position, Quaternion.identity); } } } __result = list; } } } public static class SkillsChanges { [HarmonyPatch(typeof(Skills), "OnDeath")] private static class OnDeath_Patch { private static bool Prefix(Skills __instance) { if (((Character)__instance.m_player).m_seman.HaveStatusEffect(SEMan.s_statusEffectSoftDeath)) { return false; } if (Deathlink.pcfg().DeathStyle.skillLossOnDeath) { float num = Mathf.Lerp(Deathlink.pcfg().DeathStyle.maxSkillLossPercentage, Deathlink.pcfg().DeathStyle.minSkillLossPercentage, DeathProgressionSkill.DeathSkillCalculatePercentWithBonus()); Logger.LogDebug($"{num}"); LowerConfigurableSkills(__instance, num); } return false; } private static void LowerConfigurableSkills(Skills skills, float factor) { //IL_001d: 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) foreach (KeyValuePair<SkillType, Skill> skillDatum in skills.m_skillData) { if (skills_to_avoid_standard_death_penalty.Contains(skillDatum.Key)) { Logger.LogDebug($"Skipping lowering skill {skillDatum.Key} current level: {skillDatum.Value.m_level}"); continue; } float num = skillDatum.Value.m_level * factor; Skill value = skillDatum.Value; value.m_level -= num; skillDatum.Value.m_accumulator = 0f; } if (Deathlink.RustyAlmanacClassesLoaded && ValConfig.EnableAlmanacClassesXPLossOnDeath.Value) { int level = ClassesAPI.GetLevel(); int num2 = Mathf.RoundToInt((float)level * factor * 500f * ValConfig.AlmanacClassesXPLossScale.Value) * -1; Logger.LogDebug($"Almanac (lvl {level}) XP Loss: {num2}"); ClassesAPI.AddEXP(num2); } Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)1, "$msg_skills_lowered", 0, (Sprite)null); } } } [HarmonyPatch(typeof(Skills), "RaiseSkill")] private static class SkillRaisePatch { private static void Prefix(Skills __instance, SkillType skillType, float factor) { //IL_0005: 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) float skillBonusLazyCache = Deathlink.pcfg().GetSkillBonusLazyCache(skillType); Logger.LogDebug($"{skillType} skillGain Modified {skillBonusLazyCache}"); if (skillBonusLazyCache != 0f) { factor *= skillBonusLazyCache; } } } public static List<SkillType> skills_to_avoid_standard_death_penalty = new List<SkillType>(); } public static class OnDeathChanges { [HarmonyPatch(typeof(Player))] public static class OnDeath_Tombstone_Patch { [HarmonyTranspiler] [HarmonyPatch("OnDeath")] private static IEnumerable<CodeInstruction> ConstructorTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown CodeMatcher val = new CodeMatcher(instructions, generator); val.MatchStartForward((CodeMatch[])(object)new CodeMatch[2] { new CodeMatch((OpCode?)OpCodes.Ldarg_0, (object)null, (string)null), new CodeMatch((OpCode?)OpCodes.Call, (object)AccessTools.Method(typeof(Player), "CreateTombStone", (Type[])null, (Type[])null), (string)null) }).Advance(1).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { Transpilers.EmitDelegate<Action<Player>>((Action<Player>)ModifyDeath) }) .CreateLabelOffset(out var label, 4) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Br, (object)label) }) .ThrowIfNotMatch("Unable to patch Deathlink player death changes.", Array.Empty<CodeMatch>()); return val.Instructions(); } private static void ModifyDeath(Player __instance) { TombstoneOnDeath(__instance); FoodLossOnDeath(__instance); } public static void FoodLossOnDeath(Player instance) { if (!Deathlink.pcfg().DeathStyle.foodLossOnDeath) { return; } if (Deathlink.pcfg().DeathStyle.foodLossUsesDeathlink && instance.m_foods.Count > 0) { float num = DeathProgressionSkill.DeathSkillCalculatePercentWithBonus(); if (num >= 0.9f) { return; } if (num >= 0.6f && num < 0.9f) { instance.m_foods.Remove(instance.m_foods[0]); } else if (num > 0.3f && num < 0.6f) { instance.m_foods.Remove(instance.m_foods[0]); if (instance.m_foods.Count > 0) { instance.m_foods.Remove(instance.m_foods[0]); } } else { instance.m_foods.Clear(); } } else { instance.m_foods.Clear(); } } public static void TombstoneOnDeath(Player instance) { //IL_049a: Unknown result type (might be due to invalid IL or missing references) //IL_04a5: Unknown result type (might be due to invalid IL or missing references) //IL_0549: Unknown result type (might be due to invalid IL or missing references) //IL_0578: Unknown result type (might be due to invalid IL or missing references) //IL_057e: Unknown result type (might be due to invalid IL or missing references) List<ItemData> list = new List<ItemData>(); new List<ItemData>(); List<ItemData> allItems = ((Humanoid)instance).m_inventory.GetAllItems(); List<ItemData> list2 = new List<ItemData>(); List<ItemData> list3 = new List<ItemData>(); Inventory inventory = ((Humanoid)instance).m_inventory; List<ItemData> list4 = new List<ItemData>(); List<ItemData> list5 = new List<ItemData>(); GameObject val = null; string[] source = ValConfig.ItemsNotSkillChecked.Value.Split(new char[1] { ',' }); foreach (ItemData item in allItems) { if ((Object)(object)item.m_dropPrefab != (Object)null && source.Contains(((Object)item.m_dropPrefab).name)) { list2.Add(item); } else { list3.Add(item); } } float num = DeathProgressionSkill.DeathSkillCalculatePercentWithBonus(); int num2 = Mathf.RoundToInt((float)(Deathlink.pcfg().DeathStyle.maxItemsKept - Deathlink.pcfg().DeathStyle.minItemsKept) * num + (float)Deathlink.pcfg().DeathStyle.minItemsKept); int num3 = Mathf.RoundToInt((float)(Deathlink.pcfg().DeathStyle.maxEquipmentKept - Deathlink.pcfg().DeathStyle.minEquipmentKept) * num + (float)Deathlink.pcfg().DeathStyle.minEquipmentKept); int remainingsaves = num2 + num3; List<ItemData> list6 = Deathlink.shuffleList(list3.GetEquipment()); if (Deathlink.AzuEPILoaded) { foreach (ItemData quickSlotsItem in API.GetQuickSlotsItems()) { if (source.Contains(quickSlotsItem.m_shared.m_name)) { list2.Add(quickSlotsItem); } else { list3.Add(quickSlotsItem); } } } int equipment_saved_count = 0; foreach (ItemData item2 in list6) { if (remainingsaves > 0) { if (RemoveEquipmentByStyle(equipment_saved_count, num3, instance, item2, list, remainingsaves, out remainingsaves, out equipment_saved_count)) { list.Add(item2); } else { list4.Add(item2); } } else { list4.Add(item2); } } List<ItemData> list7 = Deathlink.shuffleList(list3.GetNotEquipment()); if (remainingsaves > 0) { foreach (ItemData item3 in list7) { if (remainingsaves > 0 && num2 > 0) { Logger.LogDebug("Saving " + item3.m_shared.m_name); list.Add(item3); remainingsaves--; num2--; } else { list4.Add(item3); } } } else { list4.AddRange(list7); } if (Deathlink.AzuEPILoaded) { Logger.LogDebug($"Quickslot items found {API.GetQuickSlotsItems().Count}"); foreach (ItemData quickSlotsItem2 in API.GetQuickSlotsItems()) { if (!list.Contains(quickSlotsItem2)) { Logger.LogDebug("Removing quickslot item that was not saved " + quickSlotsItem2.m_shared.m_name); list4.Add(quickSlotsItem2); } } } switch (Deathlink.pcfg().DeathStyle.nonSkillCheckedItemAction) { case DataObjects.NonSkillCheckedItemAction.Tombstone: Logger.LogDebug($"Dropping non-skill-checked items on death ({list2.Count()})"); list5.AddRange(list2); break; case DataObjects.NonSkillCheckedItemAction.Destroy: Logger.LogDebug($"Destroying non-skill-checked items on death ({list2.Count()})"); list4.AddRange(list2); break; case DataObjects.NonSkillCheckedItemAction.Save: Logger.LogDebug($"Saving non-skill-checked items on death ({list2.Count()})"); list.AddRange(list2); break; } bool flag = false; if (Deathlink.pcfg().DeathStyle.itemLossStyle == DataObjects.ItemLossStyle.None && list4.Count > 0) { Logger.LogDebug($"Items failed skillcheck {list4.Count} items on death, dropping to tombestone."); list5.AddRange(list4); } foreach (ItemData item4 in list4) { ((Humanoid)instance).m_inventory.RemoveItem(item4); } if (Deathlink.pcfg().DeathStyle.itemSavedStyle == DataObjects.ItemSavedStyle.Tombstone && list.Count > 0) { Logger.LogDebug($"Saving {list.Count} Items to tombstone"); list5.AddRange(list); } if (list5.Count > 0) { Logger.LogDebug("Saving droppable items to tombstone"); flag = true; val = Object.Instantiate<GameObject>(instance.m_tombstone, ((Character)instance).GetCenterPoint(), ((Component)instance).transform.rotation); AddItemsToTombstone(val.GetComponent<Container>().GetInventory(), list5); foreach (ItemData item5 in list5) { ((Humanoid)instance).m_inventory.RemoveItem(item5); } TombStone component = val.GetComponent<TombStone>(); PlayerProfile playerProfile = Game.instance.GetPlayerProfile(); string name = playerProfile.GetName(); long playerID = playerProfile.GetPlayerID(); component.Setup(name, playerID); inventory.Changed(); } if (ValConfig.ShowDeathMapMarker.Value && flag) { Minimap.instance.AddPin(((Component)instance).transform.position, (PinType)4, $"$hud_mapday {EnvMan.instance.GetDay(ZNet.instance.GetTimeSeconds())}", true, false, 0L, default(PlatformUserID)); } } public static void AddItemsToTombstone(Inventory tombstone, List<ItemData> transferItems) { tombstone.m_height = (tombstone.m_width = Mathf.RoundToInt(Mathf.Sqrt((float)transferItems.Count())) + 1); foreach (ItemData transferItem in transferItems) { tombstone.m_inventory.Add(transferItem); } tombstone.Changed(); } internal static bool RemoveEquipmentByStyle(int equipment_saved, int max_equipment_savable, Player instance, ItemData equipment, List<ItemData> saved_equipment, int numberOfItemsSavable, out int remainingsaves, out int equipment_saved_count) { equipment_saved_count = 0; remainingsaves = 0; if (numberOfItemsSavable <= 0) { return false; } if (equipment_saved >= max_equipment_savable) { Logger.LogDebug($"Max equipment retained ({max_equipment_savable}) reached, removing {((Object)equipment.m_dropPrefab).name}"); return false; } Logger.LogDebug($"Saving equipment remaining savable?({numberOfItemsSavable}) {((Object)equipment.m_dropPrefab).name}"); equipment_saved_count = equipment_saved + 1; remainingsaves = numberOfItemsSavable - 1; return true; } } } } namespace Deathlink.Common { public class DataObjects { public enum ItemLossStyle { None, DestroyNonWeaponArmor, DeathlinkBased, DestroyAll } public enum ItemSavedStyle { OnCharacter, Tombstone } public enum ResourceGainTypes { Kills, Harvesting } public enum NonSkillCheckedItemAction { Destroy, Tombstone, Save } public class DeathProgressionDetails { public bool foodLossOnDeath = true; public bool foodLossUsesDeathlink = true; public int minItemsKept; public int maxItemsKept; public int minEquipmentKept; public int maxEquipmentKept; public bool skillLossOnDeath = true; public float maxSkillLossPercentage; public float minSkillLossPercentage; public ItemLossStyle itemLossStyle; public ItemSavedStyle itemSavedStyle; public NonSkillCheckedItemAction nonSkillCheckedItemAction = NonSkillCheckedItemAction.Tombstone; } public class DeathResourceModifier { public bool skillInfluence { get; set; } = true; public List<string> prefabs { get; set; } public float bonusModifer { get; set; } public List<ResourceGainTypes> bonusActions { get; set; } } public class DeathSkillModifier { public bool skillInfluence { get; set; } = true; public SkillType skill { get; set; } public float bonusModifer { get; set; } } public class DeathLootModifier { private bool skillInfluence { get; set; } = true; public string prefab { get; set; } public float chance { get; set; } public int amount { get; set; } = 1; public List<ResourceGainTypes> bonusActions { get; set; } } public class DeathChoiceLevel { private Dictionary<SkillType, float> CalculatedSkillMods = new Dictionary<SkillType, float>(); private Dictionary<string, float> CalculatedResourceMods = new Dictionary<string, float>(); private bool CalculatedResourceModsCached; private Dictionary<GameObject, Tuple<float, int>> KillLootModifiers = new Dictionary<GameObject, Tuple<float, int>>(); private bool CalculatedKillLootModifiersCached; private Dictionary<GameObject, Tuple<float, int>> ResourceLootModifiers = new Dictionary<GameObject, Tuple<float, int>>(); private bool CalculatedHarvestLootModifiersCached; public string DisplayName { get; set; } public DeathProgressionDetails DeathStyle { get; set; } public float DeathSkillRate { get; set; } = 1f; public Dictionary<string, DeathResourceModifier> ResourceModifiers { get; set; } public Dictionary<string, DeathSkillModifier> SkillModifiers { get; set; } public Dictionary<string, DeathLootModifier> DeathLootModifiers { get; set; } public List<KeyValuePair<GameObject, int>> RollKillLoot() { if (!CalculatedKillLootModifiersCached) { if (DeathLootModifiers != null && DeathLootModifiers.Count > 0) { foreach (KeyValuePair<string, DeathLootModifier> deathLootModifier in DeathLootModifiers) { if (deathLootModifier.Value.bonusActions.Contains(ResourceGainTypes.Kills)) { GameObject prefab = PrefabManager.Instance.GetPrefab(deathLootModifier.Value.prefab); if ((Object)(object)prefab == (Object)null) { Logger.LogWarning("Could not find prefab " + deathLootModifier.Value.prefab + " while building kill loot table, it will be skipped."); } else { KillLootModifiers.Add(prefab, new Tuple<float, int>(deathLootModifier.Value.chance, deathLootModifier.Value.amount)); } } } } CalculatedKillLootModifiersCached = true; } List<KeyValuePair<GameObject, int>> list = new List<KeyValuePair<GameObject, int>>(); foreach (KeyValuePair<GameObject, Tuple<float, int>> killLootModifier in KillLootModifiers) { float value = Random.value; Logger.LogDebug($"Rolling chance loot for: {((Object)killLootModifier.Key.gameObject).name} {value} < {killLootModifier.Value.Item1}"); if (value < killLootModifier.Value.Item1) { list.Add(new KeyValuePair<GameObject, int>(killLootModifier.Key, killLootModifier.Value.Item2)); } } return list; } public List<KeyValuePair<GameObject, int>> RollHarvestLoot() { if (!CalculatedHarvestLootModifiersCached) { if (DeathLootModifiers != null && DeathLootModifiers.Count > 0) { foreach (KeyValuePair<string, DeathLootModifier> deathLootModifier in DeathLootModifiers) { if (deathLootModifier.Value.bonusActions.Contains(ResourceGainTypes.Harvesting)) { GameObject prefab = PrefabManager.Instance.GetPrefab(deathLootModifier.Value.prefab); if ((Object)(object)prefab == (Object)null) { Logger.LogWarning("Could not find prefab " + deathLootModifier.Value.prefab + " while building harvest loot table, it will be skipped."); } else { ResourceLootModifiers.Add(prefab, new Tuple<float, int>(deathLootModifier.Value.chance, deathLootModifier.Value.amount)); } } } } CalculatedHarvestLootModifiersCached = true; } List<KeyValuePair<GameObject, int>> list = new List<KeyValuePair<GameObject, int>>(); foreach (KeyValuePair<GameObject, Tuple<float, int>> resourceLootModifier in ResourceLootModifiers) { if (Random.value < resourceLootModifier.Value.Item1) { list.Add(new KeyValuePair<GameObject, int>(resourceLootModifier.Key, resourceLootModifier.Value.Item2)); } } return list; } public float GetResouceEarlyCache(string prefab) { if (!CalculatedResourceModsCached) { Logger.LogDebug("Building cache entry for " + prefab); if (ResourceModifiers != null) { foreach (KeyValuePair<string, DeathResourceModifier> resourceModifier in ResourceModifiers) { if (resourceModifier.Value.prefabs == null) { continue; } foreach (string prefab2 in resourceModifier.Value.prefabs) { Logger.LogDebug($"Building cache entry for {prefab2} - {resourceModifier.Value.bonusModifer}"); CalculatedResourceMods.Add(prefab2, resourceModifier.Value.bonusModifer); } } } CalculatedResourceModsCached = true; } if (CalculatedResourceMods.ContainsKey(prefab)) { return CalculatedResourceMods[prefab]; } return 1f; } public float GetResouceEarlyCache(GameObject prefab) { if ((Object)(object)prefab == (Object)null) { return 1f; } return GetResouceEarlyCache(((Object)prefab).name); } public float GetSkillBonusLazyCache(SkillType skilltype) { //IL_0006: 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Invalid comparison between Unknown and I4 //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) if (CalculatedSkillMods.TryGetValue(skilltype, out var value)) { return value; } float num = 0f; if (SkillModifiers != null && SkillModifiers.Count > 0) { foreach (KeyValuePair<string, DeathSkillModifier> skillModifier in SkillModifiers) { if ((int)skillModifier.Value.skill == 999 || skillModifier.Value.skill == skilltype) { num += skillModifier.Value.bonusModifer; } } } CalculatedSkillMods.Add(skilltype, num); return num; } public string GetLootModifiersDescription() { StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair<string, DeathLootModifier> deathLootModifier in DeathLootModifiers) { stringBuilder.AppendLine(Localization.instance.Localize(string.Format("<color={0}>{1}%</color> $loot_desc_pt1 {2} $loot_desc_pt2 {3}", "#b9f2ff", deathLootModifier.Value.chance * 100f, deathLootModifier.Key, string.Join(",", deathLootModifier.Value.bonusActions)))); } return stringBuilder.ToString(); } public string GetSkillModiferDescription() { StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair<string, DeathSkillModifier> skillModifier in SkillModifiers) { if (skillModifier.Value.bonusModifer > 1f) { stringBuilder.AppendLine(Localization.instance.Localize(string.Format("{0} +<color={1}>{2}%</color> $xp", skillModifier.Key, "#b9f2ff", Mathf.Round((skillModifier.Value.bonusModifer - 1f) * 100f)))); } else { stringBuilder.AppendLine(Localization.instance.Localize(string.Format("{0} -<color={1}>{2}%</color> $xp", skillModifier.Key, "#ff4040", Mathf.Round((1f - skillModifier.Value.bonusModifer) * 100f)))); } } return stringBuilder.ToString(); } public string GetResourceModiferDescription() { StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair<string, DeathResourceModifier> resourceModifier in ResourceModifiers) { if (resourceModifier.Value.bonusModifer > 1f) { stringBuilder.AppendLine(Localization.instance.Localize(string.Format("{0} $drops <color={1}>{2}%</color> $more {3}", resourceModifier.Key, "#b9f2ff", (resourceModifier.Value.bonusModifer - 1f) * 100f, string.Join(",", resourceModifier.Value.bonusActions)))); } else { stringBuilder.AppendLine(Localization.instance.Localize(string.Format("{0} $drops <color={1}{2}%</color> $less {3}", resourceModifier.Key, "#ff4040", (1f - resourceModifier.Value.bonusModifer) * 100f, string.Join(",", resourceModifier.Value.bonusActions)))); } } return stringBuilder.ToString(); } public string GetDeathStyleDescription() { StringBuilder stringBuilder = new StringBuilder(); switch (DeathStyle.itemLossStyle) { case ItemLossStyle.None: stringBuilder.AppendLine(Localization.instance.Localize("$no_item_loss")); break; case ItemLossStyle.DestroyNonWeaponArmor: stringBuilder.AppendLine(Localization.instance.Localize("$no_equipment_loss")); break; case ItemLossStyle.DestroyAll: stringBuilder.AppendLine(Localization.instance.Localize("$all_item_loss")); break; case ItemLossStyle.DeathlinkBased: stringBuilder.AppendLine(Localization.instance.Localize("$limited_saved_deathlink")); stringBuilder.AppendLine(Localization.instance.Localize(string.Format("$equipment_kept <color={0}>{1}</color> - <color={2}>{3}</color>", "#b9f2ff", DeathStyle.minEquipmentKept, "#b9f2ff", DeathStyle.maxEquipmentKept))); stringBuilder.AppendLine(Localization.instance.Localize(string.Format("$items_kept <color={0}>{1}</color> - <color={2}>{3}</color>", "#b9f2ff", DeathStyle.minItemsKept, "#b9f2ff", DeathStyle.maxItemsKept))); break; } if (DeathStyle.itemLossStyle != ItemLossStyle.DestroyAll) { if (DeathStyle.itemSavedStyle == ItemSavedStyle.OnCharacter) { stringBuilder.AppendLine(Localization.instance.Localize("$saved_to_character")); } else { stringBuilder.AppendLine(Localization.instance.Localize("$saved_to_tombstone")); } if (DeathStyle.nonSkillCheckedItemAction == NonSkillCheckedItemAction.Tombstone) { stringBuilder.AppendLine(Localization.instance.Localize("$non_skill_items_tombstone")); } if (DeathStyle.nonSkillCheckedItemAction == NonSkillCheckedItemAction.Save) { stringBuilder.AppendLine(Localization.instance.Localize("$non_skill_items_character")); } if (DeathStyle.nonSkillCheckedItemAction == NonSkillCheckedItemAction.Destroy) { stringBuilder.AppendLine(Localization.instance.Localize("$non_skill_items_destroy")); } } if (DeathStyle.foodLossOnDeath) { if (DeathStyle.foodLossUsesDeathlink) { stringBuilder.AppendLine(Localization.instance.Localize("$food_loss_deathlink")); } else { stringBuilder.AppendLine(Localization.instance.Localize("$food_loss")); } } if (DeathStyle.maxSkillLossPercentage == DeathStyle.minSkillLossPercentage) { stringBuilder.AppendLine(Localization.instance.Localize(string.Format("$skill_loss_desc <color={0}>{1}%</color>", "#ff4040", DeathStyle.maxSkillLossPercentage * 100f))); } else { stringBuilder.AppendLine(Localization.instance.Localize(string.Format("$skill_loss_desc <color={0}>{1}%</color> - <color={2}>{3}%</color> $influenced_by_deathlink", "#ff4040", DeathStyle.maxSkillLossPercentage * 100f, "#ff4040", DeathStyle.minSkillLossPercentage * 100f))); } return stringBuilder.ToString(); } } public class DeathConfiguration { public string DeathChoiceLevel { get; set; } } public class PlayerDeathConfiguration { public Dictionary<long, DeathConfiguration> selectedDeathStyle { get; set; } } public abstract class ZNetProperty<T> { protected readonly ZNetView zNetView; public string Key { get; private set; } public T DefaultValue { get; private set; } protected ZNetProperty(string key, ZNetView zNetView, T defaultValue) { Key = key; DefaultValue = defaultValue; this.zNetView = zNetView; } private void ClaimOwnership() { if (!zNetView.IsOwner()) { zNetView.ClaimOwnership(); } } public void Set(T value) { SetValue(value); } public void ForceSet(T value) { ClaimOwnership(); Set(value); } public abstract T Get(); protected abstract void SetValue(T value); } public class BoolZNetProperty : ZNetProperty<bool> { public BoolZNetProperty(string key, ZNetView zNetView, bool defaultValue) : base(key, zNetView, defaultValue) { } public override bool Get() { return zNetView.GetZDO().GetBool(base.Key, base.DefaultValue); } protected override void SetValue(bool value) { zNetView.GetZDO().Set(base.Key, value); } } public class IntZNetProperty : ZNetProperty<int> { public IntZNetProperty(string key, ZNetView zNetView, int defaultValue) : base(key, zNetView, defaultValue) { } public override int Get() { return zNetView.GetZDO().GetInt(base.Key, base.DefaultValue); } protected override void SetValue(int value) { zNetView.GetZDO().Set(base.Key, value); } } public class StringZNetProperty : ZNetProperty<string> { public StringZNetProperty(string key, ZNetView zNetView, string defaultValue) : base(key, zNetView, defaultValue) { } public override string Get() { return zNetView.GetZDO().GetString(base.Key, base.DefaultValue); } protected override void SetValue(string value) { zNetView.GetZDO().Set(base.Key, value); } } public class Vector3ZNetProperty : ZNetProperty<Vector3> { public Vector3ZNetProperty(string key, ZNetView zNetView, Vector3 defaultValue) : base(key, zNetView, defaultValue) { }//IL_0003: Unknown result type (might be due to invalid IL or missing references) public override Vector3 Get() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) return zNetView.GetZDO().GetVec3(base.Key, base.DefaultValue); } protected override void SetValue(Vector3 value) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) zNetView.GetZDO().Set(base.Key, value); } } public class DictionaryZNetProperty : ZNetProperty<Dictionary<SkillType, float>> { private BinaryFormatter binFormatter = new BinaryFormatter(); public DictionaryZNetProperty(string key, ZNetView zNetView, Dictionary<SkillType, float> defaultValue) : base(key, zNetView, defaultValue) { } public override Dictionary<SkillType, float> Get() { byte[] byteArray = zNetView.GetZDO().GetByteArray(base.Key, (byte[])null); if (byteArray == null) { return new Dictionary<SkillType, float>(); } MemoryStream serializationStream = new MemoryStream(byteArray); return (Dictionary<SkillType, float>)binFormatter.Deserialize(serializationStream); } protected override void SetValue(Dictionary<SkillType, float> value) { MemoryStream memoryStream = new MemoryStream(); binFormatter.Serialize(memoryStream, value); zNetView.GetZDO().Set(base.Key, memoryStream.ToArray()); } public void UpdateDictionary() { } } public class ZDOIDZNetProperty : ZNetProperty<ZDOID> { public ZDOIDZNetProperty(string key, ZNetView zNetView, ZDOID defaultValue) : base(key, zNetView, defaultValue) { }//IL_0003: Unknown result type (might be due to invalid IL or missing references) public override ZDOID Get() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) return zNetView.GetZDO().GetZDOID(base.Key); } protected override void SetValue(ZDOID value) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) zNetView.GetZDO().Set(base.Key, value); } } public static IDeserializer yamldeserializer = ((BuilderSkeleton<DeserializerBuilder>)new DeserializerBuilder()).WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); public static ISerializer yamlserializer = ((BuilderSkeleton<SerializerBuilder>)new SerializerBuilder()).WithNamingConvention(CamelCaseNamingConvention.Instance).DisableAliases().ConfigureDefaultValuesHandling((DefaultValuesHandling)2) .Build(); private const string color_good = "#b9f2ff"; private const string color_bad = "#ff4040"; } internal static class DeathConfigurationData { [HarmonyPatch(typeof(Player))] public static class SetupDeathLinkPlayerSpecificConfigPatch { [HarmonyPostfix] [HarmonyPatch("SetPlayerID")] private static void Postfix() { CheckAndSetPlayerDeathConfig(); } } public static readonly Dictionary<string, DataObjects.DeathChoiceLevel> defaultDeathLevels = new Dictionary<string, DataObjects.DeathChoiceLevel> { { "Vanilla", new DataObjects.DeathChoiceLevel { DisplayName = "Vanilla", DeathStyle = new DataObjects.DeathProgressionDetails { itemLossStyle = DataObjects.ItemLossStyle.None, foodLossOnDeath = true, itemSavedStyle = DataObjects.ItemSavedStyle.Tombstone, minSkillLossPercentage = 0.05f, maxSkillLossPercentage = 0.05f }, DeathLootModifiers = new Dictionary<string, DataObjects.DeathLootModifier>(), ResourceModifiers = new Dictionary<string, DataObjects.DeathResourceModifier>(), SkillModifiers = new Dictionary<string, DataObjects.DeathSkillModifier>() } }, { "Rougelike1", new DataObjects.DeathChoiceLevel { DisplayName = "ShieldBearer", DeathStyle = new DataObjects.DeathProgressionDetails { itemLossStyle = DataObjects.ItemLossStyle.DeathlinkBased, foodLossUsesDeathlink = true, itemSavedStyle = DataObjects.ItemSavedStyle.Tombstone, minEquipmentKept = 3, maxEquipmentKept = 9, minItemsKept = 3, maxItemsKept = 15, minSkillLossPercentage = 0.03f, maxSkillLossPercentage = 0.13f }, DeathLootModifiers = new Dictionary<string, DataObjects.DeathLootModifier>(), ResourceModifiers = new Dictionary<string, DataObjects.DeathResourceModifier> { { "Wood", new DataObjects.DeathResourceModifier { prefabs = new List<string> { "Wood", "FineWood", "RoundLog", "YggdrasilWood", "Blackwood" }, bonusModifer = 1.1f, bonusActions = new List<DataObjects.ResourceGainTypes> { DataObjects.ResourceGainTypes.Harvesting } } } }, SkillModifiers = new Dictionary<string, DataObjects.DeathSkillModifier> { { "All", new DataObjects.DeathSkillModifier { bonusModifer = 1.05f, skill = (SkillType)999 } } } } }, { "Rougelike2", new DataObjects.DeathChoiceLevel { DisplayName = "Raider", DeathStyle = new DataObjects.DeathProgressionDetails { itemLossStyle = DataObjects.ItemLossStyle.DeathlinkBased, itemSavedStyle = DataObjects.ItemSavedStyle.Tombstone, minEquipmentKept = 2, maxEquipmentKept = 6, minSkillLossPercentage = 0.02f, maxSkillLossPercentage = 0.14f }, DeathLootModifiers = new Dictionary<string, DataObjects.DeathLootModifier>(), ResourceModifiers = new Dictionary<string, DataObjects.DeathResourceModifier> { { "Wood", new DataObjects.DeathResourceModifier { prefabs = new List<string> { "Wood", "FineWood", "RoundLog", "YggdrasilWood", "Blackwood" }, bonusModifer = 1.2f, bonusActions = new List<DataObjects.ResourceGainTypes> { DataObjects.ResourceGainTypes.Harvesting } } }, { "Ore", new DataObjects.DeathResourceModifier { prefabs = new List<string> { "CopperOre", "TinOre", "IronScrap", "SilverOre", "BlackMetalScrap", "CopperScrap", "FlametalOreNew" }, bonusModifer = 1.2f, bonusActions = new List<DataObjects.ResourceGainTypes> { DataObjects.ResourceGainTypes.Harvesting } } } }, SkillModifiers = new Dictionary<string, DataObjects.DeathSkillModifier> { { "All", new DataObjects.DeathSkillModifier { bonusModifer = 1.1f, skill = (SkillType)999 } } } } }, { "Rougelike3", new DataObjects.DeathChoiceLevel { DisplayName = "Berserker", DeathStyle = new DataObjects.DeathProgressionDetails { itemLossStyle = DataObjects.ItemLossStyle.DeathlinkBased, itemSavedStyle = DataObjects.ItemSavedStyle.OnCharacter, minEquipmentKept = 0, maxEquipmentKept = 3, minSkillLossPercentage = 0.05f, maxSkillLossPercentage = 0.2f }, DeathLootModifiers = new Dictionary<string, DataObjects.DeathLootModifier> { { "AmberPearl", new DataObjects.DeathLootModifier { chance = 0.05f, prefab = "AmberPearl", bonusActions = new List<DataObjects.ResourceGainTypes> { DataObjects.ResourceGainTypes.Kills } } } }, ResourceModifiers = new Dictionary<string, DataObjects.DeathResourceModifier> { { "Wood", new DataObjects.DeathResourceModifier { prefabs = new List<string> { "Wood", "FineWood", "RoundLog", "YggdrasilWood", "Blackwood" }, bonusModifer = 1.5f, bonusActions = new List<DataObjects.ResourceGainTypes> { DataObjects.ResourceGainTypes.Harvesting } } }, { "Ore", new DataObjects.DeathResourceModifier { prefabs = new List<string> { "CopperOre", "TinOre", "IronScrap", "SilverOre", "BlackMetalScrap", "CopperScrap", "FlametalOreNew" }, bonusModifer = 1.5f, bonusActions = new List<DataObjects.ResourceGainTypes> { DataObjects.ResourceGainTypes.Harvesting } } } }, SkillModifiers = new Dictionary<string, DataObjects.DeathSkillModifier> { { "All", new DataObjects.DeathSkillModifier { bonusModifer = 1.2f, skill = (SkillType)999 } } } } }, { "Hardcore", new DataObjects.DeathChoiceLevel { DisplayName = "Deathbringer", DeathStyle = new DataObjects.DeathProgressionDetails { itemLossStyle = DataObjects.ItemLossStyle.DestroyAll, minSkillLossPercentage = 0.05f, maxSkillLossPercentage = 0.25f }, DeathLootModifiers = new Dictionary<string, DataObjects.DeathLootModifier> { { "AmberPearl", new DataObjects.DeathLootModifier { chance = 0.05f, prefab = "AmberPearl", bonusActions = new List<DataObjects.ResourceGainTypes> { DataObjects.ResourceGainTypes.Kills } } }, { "SmallHealthPotion", new DataObjects.DeathLootModifier { chance = 0.01f, prefab = "MeadHealthMinor", bonusActions = new List<DataObjects.ResourceGainTypes> { DataObjects.ResourceGainTypes.Kills } } } }, ResourceModifiers = new Dictionary<string, DataObjects.DeathResourceModifier> { { "Wood", new DataObjects.DeathResourceModifier { prefabs = new List<string> { "Wood", "FineWood", "RoundLog", "YggdrasilWood", "Blackwood" }, bonusModifer = 2f, bonusActions = new List<DataObjects.ResourceGainTypes> { DataObjects.ResourceGainTypes.Harvesting } } }, { "Stone", new DataObjects.DeathResourceModifier { prefabs = new List<string> { "Flint", "Stone", "BlackMarble", "Grausten" }, bonusModifer = 2f, bonusActions = new List<DataObjects.ResourceGainTypes> { DataObjects.ResourceGainTypes.Harvesting } } }, { "Ore", new DataObjects.DeathResourceModifier { prefabs = new List<string> { "CopperOre", "TinOre", "IronScrap", "SilverOre", "BlackMetalScrap", "CopperScrap", "FlametalOreNew" }, bonusModifer = 2f, bonusActions = new List<DataObjects.ResourceGainTypes> { DataObjects.ResourceGainTypes.Harvesting } } } }, SkillModifiers = new Dictionary<string, DataObjects.DeathSkillModifier> { { "All", new DataObjects.DeathSkillModifier { bonusModifer = 1.3f, skill = (SkillType)999 } } } } } }; public static Dictionary<long, DataObjects.DeathConfiguration> playerSettings = new Dictionary<long, DataObjects.DeathConfiguration>(); public static Dictionary<string, DataObjects.DeathChoiceLevel> DeathLevels = defaultDeathLevels; public static DataObjects.DeathChoiceLevel playerDeathConfiguration = new DataObjects.DeathChoiceLevel { DeathStyle = new DataObjects.DeathProgressionDetails { foodLossOnDeath = true, foodLossUsesDeathlink = false, itemLossStyle = DataObjects.ItemLossStyle.None, minItemsKept = 0, maxItemsKept = 0, minEquipmentKept = 0, maxEquipmentKept = 0, skillLossOnDeath = true, maxSkillLossPercentage = 0.05f, minSkillLossPercentage = 0.05f, itemSavedStyle = DataObjects.ItemSavedStyle.Tombstone, nonSkillCheckedItemAction = DataObjects.NonSkillCheckedItemAction.Tombstone } }; internal static void Init() { try { UpdateDeathLevelsConfig(File.ReadAllText(ValConfig.deathChoicesPath)); } catch (Exception arg) { Logger.LogWarning((object)$"There was an error updating the Death choice Level values, defaults will be used. Exception: {arg}"); } try { UpdatePlayerConfigSettings(File.ReadAllText(ValConfig.playerSettingsPath)); } catch (Exception arg2) { Logger.LogWarning((object)$"There was an error updating the player choice configs, defaults will be used. Exception: {arg2}"); } } public static void CheckAndSetPlayerDeathConfig() { if (!((Object)(object)Player.m_localPlayer == (Object)null)) { CheckAndSetPlayerDeathConfig(Player.m_localPlayer.GetPlayerID()); } } public static void CheckAndSetPlayerDeathConfig(long playerID) { Logger.LogDebug(string.Format("Checking stored configurations for {0} {1}", playerID, string.Join(",", playerSettings.Keys))); if (playerSettings.ContainsKey(playerID)) { string deathChoiceLevel = playerSettings[playerID].DeathChoiceLevel; if (DeathLevels.ContainsKey(deathChoiceLevel)) { Logger.LogDebug("Player deathlink configurations set " + deathChoiceLevel); playerDeathConfiguration = DeathLevels[deathChoiceLevel]; } else { Logger.LogDebug("Player preference setting is not an available config, using fallback"); playerDeathConfiguration = DeathLevels.First().Value; } } } public static string PlayerSettingsDefaultConfig() { return DataObjects.yamlserializer.Serialize((object)playerSettings); } public static string DeathLevelsYamlDefaultConfig() { return DataObjects.yamlserializer.Serialize((object)DeathLevels); } public static void WriteDeathChoices() { File.WriteAllText(ValConfig.deathChoicesPath, DataObjects.yamlserializer.Serialize((object)DeathLevels)); } public static void WritePlayerChoices() { File.WriteAllText(ValConfig.playerSettingsPath, DataObjects.yamlserializer.Serialize((object)playerSettings)); } public static void UpdateDeathLevelsConfig(string rawyaml) { DeathLevels = DataObjects.yamldeserializer.Deserialize<Dictionary<string, DataObjects.DeathChoiceLevel>>(rawyaml); } public static void UpdatePlayerConfigSettings(string rawyaml) { foreach (KeyValuePair<long, DataObjects.DeathConfiguration> item in DataObjects.yamldeserializer.Deserialize<Dictionary<long, DataObjects.DeathConfiguration>>(rawyaml)) { if (!playerSettings.ContainsKey(item.Key)) { playerSettings.Add(item.Key, item.Value); } } } } internal static class Utils { public static CodeMatcher CreateLabelOffset(this CodeMatcher matcher, out Label label, int offset = 0) { return matcher.CreateLabelAt(matcher.Pos + offset, ref label); } } public class ValConfig { [CompilerGenerated] private sealed class <OnClientReceiveDeathChoiceConfigs>d__27 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public ZPackage package; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <OnClientReceiveDeathChoiceConfigs>d__27(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; DeathConfigurationData.UpdateDeathLevelsConfig(package.ReadString()); DeathConfigurationData.WriteDeathChoices(); <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <OnClientReceivePlayerSettingsConfigs>d__30 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public ZPackage package; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <OnClientReceivePlayerSettingsConfigs>d__30(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; DeathConfigurationData.UpdatePlayerConfigSettings(package.ReadString()); DeathConfigurationData.CheckAndSetPlayerDeathConfig(); <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <OnServerRecieveConfigs>d__28 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <OnServerRecieveConfigs>d__28(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <OnServerRecievePlayerSettingsConfig>d__29 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public ZPackage package; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <OnServerRecievePlayerSettingsConfig>d__29(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; DeathConfigurationData.UpdatePlayerConfigSettings(package.ReadString()); DeathConfigurationData.WritePlayerChoices(); <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public static ConfigFile cfg; public static ConfigEntry<bool> EnableDebugMode; public static ConfigEntry<string> ItemsNotSkillChecked; public static ConfigEntry<float> SkillGainOnKills; public static ConfigEntry<float> SkillGainOnBossKills; public static ConfigEntry<float> SkillGainOnCrafts; public static ConfigEntry<float> SkillGainOnResourceGathering; public static ConfigEntry<float> SkillGainOnBuilding; public static ConfigEntry<bool> ShowDeathMapMarker; public static ConfigEntry<bool> EnableAlmanacClassesXPLossOnDeath; public static ConfigEntry<float> AlmanacClassesXPLossScale; public static ConfigEntry<float> AlmanacClassesXPGainScale; private const string cfgFolder = "Deathlink"; private const string deathChoicesCfg = "DeathChoices.yaml"; private const string deathSettingsCfg = "CharacterSettings.yaml"; internal static string deathChoicesPath = Path.Combine(Paths.ConfigPath, "Deathlink", "DeathChoices.yaml"); internal static string playerSettingsPath = Path.Combine(Paths.ConfigPath, "Deathlink", "CharacterSettings.yaml"); private static CustomRPC deathChoiceRPC; private static CustomRPC characterSettingRPC; public static ConfigEntry<float> SkillProgressUpdateCheckInterval; public ValConfig(ConfigFile Config) { cfg = Config; cfg.SaveOnConfigSet = true; CreateConfigValues(Config); SetupConfigRPCs(); LoadYamlConfigs(); } public static string GetSecondaryConfigDirectoryPath() { return Directory.CreateDirectory(Path.Combine(Paths.ConfigPath, "Deathlink")).FullName; } public void SetupConfigRPCs() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_0027: Expected O, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_0053: Expected O, but got Unknown deathChoiceRPC = NetworkManager.Instance.AddRPC("DEATHLK_CH", new CoroutineHandler(OnServerRecieveConfigs), new CoroutineHandler(OnClientReceiveDeathChoiceConfigs)); characterSettingRPC = NetworkManager.Instance.AddRPC("DEATHLK_PSET", new CoroutineHandler(OnServerRecievePlayerSettingsConfig), new CoroutineHandler(OnClientReceivePlayerSettingsConfigs)); SynchronizationManager.Instance.AddInitialSynchronization(deathChoiceRPC, (Func<ZPackage>)SendDeathChoices); SynchronizationManager.Instance.AddInitialSynchronization(characterSettingRPC, (Func<ZPackage>)SendCharSettings); } private void CreateConfigValues(ConfigFile Config) { //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Expected O, but got Unknown //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Expected O, but got Unknown ItemsNotSkillChecked = BindServerConfig("DeathProgression", "ItemsNotSkillChecked", "Tin,TinOre,Copper,CopperOre,CopperScrap,Bronze,Iron,IronScrap,Silver,SilverOre,DragonEgg,chest_hildir1,chest_hildir2,chest_hildir3,BlackMetal,BlackMetalScrap,DvergrNeedle,MechanicalSpring,FlametalNew,FlametalOreNew", "List of items that are not rolled to be saved through death progression."); SkillGainOnKills = BindServerConfig("DeathSkillGain", "SkillGainOnKills", 5f, "Skill Gain from killing non-boss creatures."); SkillGainOnBossKills = BindServerConfig("DeathSkillGain", "SkillGainOnBossKills", 20f, "Skill Gain from killing boss creatures."); SkillGainOnCrafts = BindServerConfig("DeathSkillGain", "SkillGainOnCrafts", 0.8f, "Skill Gain from crafting."); SkillGainOnResourceGathering = BindServerConfig("DeathSkillGain", "SkillGainOnResourceGathering", 0.1f, "Skill Gain from resource gathering."); SkillGainOnBuilding = BindServerConfig("DeathSkillGain", "SkillGainOnBuilding", 0.5f, "Skill Gain from building."); SkillProgressUpdateCheckInterval = BindServerConfig("DeathSkillGain", "SkillProgressUpdateCheckInterval", 1f, "How frequently skill gains are computed and added. More frequently means smaller xp gains more often.", advanced: true, 0.1f, 5f); ShowDeathMapMarker = BindServerConfig("DeathTweaks", "ShowDeathMapMarker", value: true, "Whether or not a map marker is placed on your death location."); EnableAlmanacClassesXPLossOnDeath = BindServerConfig("ModIntegrations", "EnableAlmanacClassesXPLossOnDeath", value: true, "If true, XP loss also happens for characters Alamanc Class level."); AlmanacClassesXPLossScale = BindServerConfig("ModIntegrations", "AlmanacClassesXPLossScale", 1f, "How strong the XP loss for Almanac is, lower = less XP loss, higher = more XP loss."); AlmanacClassesXPGainScale = BindServerConfig("ModIntegrations", "AlmanacClassesXPGainScale", 20f, "How much Almanac Classes XP is gained based on Deathlink actions. This is gained at an inregular interval based on deathlink skill gains."); EnableDebugMode = Config.Bind<bool>("Client config", "EnableDebugMode", false, new ConfigDescription("Enables Debug logging.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdvanced = true } })); EnableDebugMode.SettingChanged += Logger.enableDebugLogging; Logger.CheckEnableDebugLogging(); } internal void LoadYamlConfigs() { string[] files = Directory.GetFiles(GetSecondaryConfigDirectoryPath()); bool flag = false; bool flag2 = false; string[] array = files; foreach (string text in array) { if (text.Contains("DeathChoices.yaml")) { Logger.LogDebug("Found Deathchoice configuration: " + text); deathChoicesPath = text; flag = true; } if (text.Contains("CharacterSettings.yaml")) { Logger.LogDebug("Found Character configuration: " + text); playerSettingsPath = text; flag2 = true; } } if (!flag) { Logger.LogDebug("Death Choices missing, recreating."); using StreamWriter streamWriter = new StreamWriter(deathChoicesPath); string value = "#################################################\n# Deathlink - Death Choice Configuration\n#################################################\n"; streamWriter.WriteLine(value); streamWriter.WriteLine(DeathConfigurationData.DeathLevelsYamlDefaultConfig()); } if (!flag2) { Logger.LogDebug("Character Settings missing, recreating."); using StreamWriter streamWriter2 = new StreamWriter(playerSettingsPath); string value2 = "#################################################\n# Deathlink - Character settings\n#################################################\n"; streamWriter2.WriteLine(value2); streamWriter2.WriteLine(DeathConfigurationData.PlayerSettingsDefaultConfig()); } SetupFileWatcher("DeathChoices.yaml"); SetupFileWatcher("CharacterSettings.yaml"); } private void SetupFileWatcher(string filtername) { FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(); fileSystemWatcher.Path = GetSecondaryConfigDirectoryPath(); fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite; fileSystemWatcher.Filter = filtername; fileSystemWatcher.Changed += UpdateConfigFileOnChange; fileSystemWatcher.Created += UpdateConfigFileOnChange; fileSystemWatcher.Renamed += UpdateConfigFileOnChange; fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; fileSystemWatcher.EnableRaisingEvents = true; } private static void UpdateConfigFileOnChange(object sender, FileSystemEventArgs e) { if (!SynchronizationManager.Instance.PlayerIsAdmin) { Logger.LogInfo("Player is not an admin, and not allowed to change local configuration. Ignoring."); } else if