using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; using UnityEngine.Networking; using neobotics.ModSdk; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("DefendYourBase")] [assembly: AssemblyConfiguration("Deploy")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+8f12218228e04526ba7be2e4e0f5dfddcf5e3a69")] [assembly: AssemblyProduct("DefendYourBase")] [assembly: AssemblyTitle("DefendYourBase")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] public sealed class ConfigurationManagerAttributes { public delegate void CustomHotkeyDrawerFunc(ConfigEntryBase setting, ref bool isCurrentlyAcceptingInput); public bool? ShowRangeAsPercent; public Action CustomDrawer; public CustomHotkeyDrawerFunc CustomHotkeyDrawer; public bool? Browsable; public string Category; public object DefaultValue; public bool? HideDefaultButton; public bool? HideSettingName; public string Description; public string DispName; public int? Order; public bool? ReadOnly; public bool? IsAdvanced; public Func ObjToStr; public Func StrToObj; } namespace neobotics.ModSdk { public class AudioHelper : MonoBehaviour { private readonly Dictionary audioClips = new Dictionary(); private Logging logger; public void Awake() { logger = Logging.GetLogger(); logger.Debug("AudidHelper.Awake"); } public void Start() { logger.Debug("AudioHelper.Start"); ZRoutedRpc.instance.Register("NeoPlayClip", (Action)RPC_NeoPlayClip); } public void RPC_NeoPlayClip(long sender, Vector3 sourcePoint, string clipName) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (ZNet.instance.IsDedicated()) { return; } try { NeoPlayClip(sourcePoint, clipName); } catch (Exception e) { logger.Error(e, stackTrace: false); } } public void Play(Vector3 sourcePoint, string clipName) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "NeoPlayClip", new object[2] { sourcePoint, clipName }); } public void NeoPlayClip(Vector3 sourcePoint, string clipName) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)Player.m_localPlayer).transform.position; NeoPlayClip(sourcePoint, position, clipName); } public void NeoPlayClip(Vector3 sourcePoint, Vector3 targetPoint, string clipName) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) if (!audioClips.TryGetValue(clipName.ToLower(), out var value)) { logger.Warning("Couldn't find audio clip for " + clipName); return; } Vector3 val = sourcePoint - targetPoint; Vector3 normalized = ((Vector3)(ref val)).normalized; float num = (Vector3.Distance(sourcePoint, targetPoint) + 1f) / 10f; AudioSource.PlayClipAtPoint(value, targetPoint + normalized * num, 1f); } public float GetClipDuration(string clipName) { if (!audioClips.TryGetValue(clipName.ToLower(), out var value)) { return -1f; } return value.length; } internal void LoadAudioResources(string resourceFolder) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) audioClips.Clear(); logger.Debug("LoadAudioResources"); string[] files = Directory.GetFiles(resourceFolder, "*.*", SearchOption.TopDirectoryOnly); foreach (string text in files) { string text2 = Path.GetExtension(text).ToLowerInvariant(); AudioType val = (AudioType)0; if (!(text2 == ".wav")) { if (text2 == ".mp3") { val = (AudioType)13; } } else { val = (AudioType)20; } if ((int)val != 0) { ((MonoBehaviour)this).StartCoroutine(LoadExternalClip(text, val)); } } } private IEnumerator LoadExternalClip(string filePath, AudioType type) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) string uri = new Uri(filePath).AbsoluteUri; string clipName = Path.GetFileName(filePath).ToLowerInvariant(); UnityWebRequest req = UnityWebRequestMultimedia.GetAudioClip(uri, type); yield return req.SendWebRequest(); if ((int)req.result == 1) { AudioClip content = DownloadHandlerAudioClip.GetContent(req); audioClips[clipName] = content; logger.Debug("Loaded external audio: " + clipName); } else { logger.Warning("Failed to load external audio: " + uri); } } } internal class CircularList { private List circle; private int index; public int Index => index; public CircularList(List source, T startingElement) { circle = new List(source); index = circle.FindIndex((T x) => x.Equals(startingElement)); } public T Next() { index = ((index < circle.Count - 1) ? (index + 1) : 0); return circle[index]; } public T Previous() { index = ((index > 0) ? (index - 1) : (circle.Count - 1)); return circle[index]; } } public static class ConfigFolderHelper { private const string ROOT = "Neobotics"; public static string GetOrCreateModConfigFolder(string modName) { string text = Path.Combine(Paths.ConfigPath, "Neobotics", modName); Directory.CreateDirectory(text); return text; } } public class ConfigMock { public bool Value { get; set; } } public class CrossPlatformRandom : Random { private const int LCG_MULTIPLIER = 134775813; private const int LCG_INCREMENT = 1; private int _seed; public float value => (float)NextDouble(); public CrossPlatformRandom() { Random random = new Random(); _seed = random.Next(); } public CrossPlatformRandom(int seed) { _seed = seed; } public float Range(int min, int max) { return Next(min, max); } public float Range(float min, float max) { return Mathf.Lerp(min, max, (float)NextDouble()); } private int GetNext() { _seed = _seed * 134775813 + 1; return _seed; } public override int Next() { return Next(int.MaxValue); } public override int Next(int maxValue) { if (maxValue < 0) { throw new ArgumentOutOfRangeException("maxValue is less than zero."); } return (int)((long)(uint)GetNext() * (long)(uint)maxValue >>> 32); } public override int Next(int minValue, int maxValue) { if (minValue > maxValue) { throw new ArgumentOutOfRangeException("minValue is greater than maxValue."); } return minValue + Next(maxValue - minValue); } public override double NextDouble() { return Sample(); } protected override double Sample() { return (double)Next() / 2147483647.0; } } internal class CustomDataWrapper { private Dictionary playerData; private Dictionary Data { get; set; } public CustomDataWrapper(Dictionary sourceData, string keyPrefix) { CustomDataWrapper customDataWrapper = this; playerData = sourceData; Data = new Dictionary(); sourceData.Keys.ToList().ForEach(delegate(string key) { if (key.StartsWith(keyPrefix)) { customDataWrapper.Data.Add(key, sourceData[key]); } }); } public void Add(string key, string value) { Data.Add(key, value); playerData.Add(key, value); } public bool Remove(string key) { return Data.Remove(key) & playerData.Remove(key); } public void Set(string key, string value) { if (Data.ContainsKey(key)) { Data[key] = value; } else { Data.Add(key, value); } if (playerData.ContainsKey(key)) { playerData[key] = value; } else { playerData.Add(key, value); } } public string Get(string key) { if (Data.ContainsKey(key)) { return Data[key]; } return null; } public bool ContainsKey(string key) { return Data.ContainsKey(key); } public void PreSaveSync() { foreach (KeyValuePair datum in Data) { if (!playerData.ContainsKey(datum.Key)) { playerData.Add(datum.Key, datum.Value); } } } } internal class DebugUtils { internal static void ObjectInspector(object o) { if (o == null) { Debug.Log((object)"Object is null"); return; } BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; Type type = o.GetType(); Debug.Log((object)("Object: " + o.ToString() + " Type " + type.Name)); PropertyInfo[] properties = type.GetProperties(bindingAttr); foreach (PropertyInfo propertyInfo in properties) { if (propertyInfo.CanRead) { Debug.Log((object)$"Property: {type.Name}.{propertyInfo.Name} = {propertyInfo.GetValue(o)}"); continue; } Debug.Log((object)("Property: " + type.Name + "." + propertyInfo.Name + " is write-only")); } FieldInfo[] fields = type.GetFields(bindingAttr); foreach (FieldInfo field in fields) { FieldPrinter(o, type, field); } } internal static void MethodInspector(object o) { if (o == null) { Debug.Log((object)"Object is null"); return; } BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; Type type = o.GetType(); Debug.Log((object)("Method: " + o.ToString() + " Type " + type.Name)); MethodInfo[] methods = type.GetMethods(bindingAttr); foreach (MethodInfo methodInfo in methods) { methodInfo.GetParameters(); string arg = string.Join(", ", (from x in methodInfo.GetParameters() select x.ParameterType?.ToString() + " " + x.Name).ToArray()); Debug.Log((object)$"{methodInfo.ReturnType} {methodInfo.Name} ({arg})"); } } private static void ItemDataInspector(ItemData item) { ObjectInspector(item); ObjectInspector(item?.m_shared); } private static void FieldPrinter(object o, Type t, FieldInfo field) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Expected O, but got Unknown try { if (field.FieldType == typeof(ItemData)) { ItemData val = (ItemData)field.GetValue(o); if (val != null) { ItemDataInspector(val); } else { Debug.Log((object)$"{t.Name}.{field.Name} = {field.GetValue(o)} [null]"); } } else if (field.FieldType == typeof(Transform)) { Transform val2 = (Transform)field.GetValue(o); if ((Object)(object)val2 != (Object)null) { Debug.Log((object)("\tTransform.parent = " + ((Object)val2.parent).name)); } else { Debug.Log((object)$"{t.Name}.{field.Name} = {field.GetValue(o)} [null]"); } } else if (field.FieldType == typeof(EffectList)) { EffectList val3 = (EffectList)field.GetValue(o); if (val3 != null) { Debug.Log((object)$"{t.Name}.{field.Name} = {field.GetValue(o)}:"); EffectData[] effectPrefabs = val3.m_effectPrefabs; foreach (EffectData val4 in effectPrefabs) { Debug.Log((object)("\tEffectData.m_prefab = " + ((Object)val4.m_prefab).name)); } } else { Debug.Log((object)$"{t.Name}.{field.Name} = {field.GetValue(o)} [null]"); } } else { Debug.Log((object)$"{t.Name}.{field.Name} = {field.GetValue(o)}"); } } catch (Exception) { Debug.Log((object)("Exception accessing " + t?.Name + "." + field?.Name)); } } internal static void GameObjectInspector(GameObject go) { if ((Object)(object)go == (Object)null) { Debug.Log((object)"\n\nGame Object is null"); return; } Debug.Log((object)("\n\nInspecting GameObject " + ((Object)go).name)); ObjectInspector(go); Component[] componentsInChildren = go.GetComponentsInChildren(); if (componentsInChildren.Length == 0) { return; } Component[] array = componentsInChildren; foreach (Component c in array) { try { ComponentInspector(c); } catch (Exception) { } } } internal static void PrintList(List l) { foreach (T item in l) { Debug.Log((object)item.ToString()); } } internal static void ComponentInspector(Component c) { string obj = ((c != null) ? ((Object)c).name : null); object obj2; if (c == null) { obj2 = null; } else { Transform transform = c.transform; if (transform == null) { obj2 = null; } else { Transform parent = transform.parent; obj2 = ((parent != null) ? ((Object)parent).name : null); } } Debug.Log((object)("\n\nInspecting Component " + obj + " with parent " + (string?)obj2)); ObjectInspector(c); } internal static void EffectsInspector(EffectList e) { EffectData[] effectPrefabs = e.m_effectPrefabs; Debug.Log((object)$"Effect list has effects {e.HasEffects()} count {effectPrefabs.Length}"); EffectData[] array = effectPrefabs; foreach (EffectData val in array) { Debug.Log((object)$"Effect Data {val} prefab name {((Object)val.m_prefab).name} prefab GameObject name {((Object)val.m_prefab.gameObject).name}"); } } internal static void PrintInventory() { foreach (ItemData allItem in ((Humanoid)Player.m_localPlayer).GetInventory().GetAllItems()) { Debug.Log((object)allItem.m_shared.m_name); } } internal static void PrintAllObjects() { ZNetScene.instance.m_prefabs.ForEach(delegate(GameObject x) { Debug.Log((object)("GameObject " + ((Object)x).name)); }); } internal static void PrintAllCharacters() { Character.GetAllCharacters().ForEach(delegate(Character x) { Debug.Log((object)("Character " + ((Object)x).name)); }); } internal static void PrintAllLayers() { string[] array = (from index in Enumerable.Range(0, 31) select LayerMask.LayerToName(index) into l where !string.IsNullOrEmpty(l) select l).ToArray(); foreach (string text in array) { Debug.Log((object)("Layer " + text + " " + Convert.ToString(LayerMask.NameToLayer(text), 2).PadLeft(32, '0'))); } } } public class DelegatedConfigEntry : DelegatedConfigEntryBase { private ConfigEntry _entry; private EventHandler rootHandler; private Action clientDelegate; private Logging Log; public ConfigEntry ConfigEntry { get { return _entry; } set { _entry = value; if (_entry != null && rootHandler != null) { _entry.SettingChanged += rootHandler; } Name = ((ConfigEntryBase)_entry).Definition.Key; Section = ((ConfigEntryBase)_entry).Definition.Section; ServerValue = ((ConfigEntryBase)_entry).GetSerializedValue(); Log.Trace("Set " + Section + " " + Name + " to serialized value " + ServerValue); } } public T Value { get { return _entry.Value; } set { _entry.Value = value; } } public DelegatedConfigEntry(bool useServerDelegate = false) : this((Action)null, useServerDelegate) { } public DelegatedConfigEntry(Action delegateHandler, bool useServerDelegate = false) { Log = Logging.GetLogger(); Log.Trace("DelegatedConfigEntry"); if (delegateHandler != null) { clientDelegate = delegateHandler; } if (useServerDelegate) { Log.Trace("Configuring server delegate"); rootHandler = delegate(object s, EventArgs e) { ServerDelegate(s, e); }; ServerConfiguration.ServerDelegatedEntries.Add(this); } else if (clientDelegate != null) { rootHandler = delegate(object s, EventArgs e) { clientDelegate(s, e); }; } } private void ServerDelegate(object sender, EventArgs args) { //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Expected O, but got Unknown Logging.GetLogger().Trace("ServerDelegate"); _entry.SettingChanged -= rootHandler; ZNet instance = ZNet.instance; bool? flag = ((instance != null) ? new bool?(instance.IsServer()) : ((bool?)null)); if (flag.HasValue) { if (flag == false && ServerConfiguration.Instance.ReceivedServerValues) { if (ServerValue != null) { ((ConfigEntryBase)_entry).SetSerializedValue(ServerValue); Log.Debug("Setting " + Name + " to server value " + ServerValue); } } else if (flag == true) { ServerValue = ((ConfigEntryBase)_entry).GetSerializedValue(); ServerConfiguration.Instance.SendConfigToAllClients(sender, (SettingChangedEventArgs)args); } } if (clientDelegate != null) { clientDelegate(sender, args); } _entry.SettingChanged += rootHandler; } public void EnableHandler(bool setActive) { if (setActive) { _entry.SettingChanged += rootHandler; } else { _entry.SettingChanged -= rootHandler; } } public bool IsKeyPressed() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (ConfigEntry is ConfigEntry val) { return InputEvaluator.IsPressed(val.Value); } Log.Error("Keyboard read attempted on non-KeyboardShortcut config."); return false; } public bool IsKeyDown() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (ConfigEntry is ConfigEntry val) { return InputEvaluator.IsDown(val.Value); } Log.Error("Keyboard read attempted on non-KeyboardShortcut config."); return false; } public bool IsKeyReleased() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (ConfigEntry is ConfigEntry val) { return InputEvaluator.IsReleased(val.Value); } Log.Error("Keyboard read attempted on non-KeyboardShortcut config."); return false; } } public class DelegatedConfigEntryBase { public string Name; public string Section; public string ServerValue; } internal class HarmonyHelper { public enum PatchType { Prefix, Postfix, Transpiler, Finalizer } private static Dictionary detectionSet = new Dictionary(); private static Dictionary unpatchMods = new Dictionary(); public static IReadOnlyDictionary DetectionSet => detectionSet; public static void GetDetectionSet(Dictionary harmonyIds) { Logging logger = Logging.GetLogger(); foreach (KeyValuePair harmonyId in harmonyIds) { if (Harmony.HasAnyPatches(harmonyId.Key)) { logger.Debug("Detected " + harmonyId.Value + " from Harmony"); if (!detectionSet.ContainsKey(harmonyId.Key)) { detectionSet.Add(harmonyId.Key, harmonyId.Value); } } else if (Chainloader.PluginInfos.ContainsKey(harmonyId.Key)) { logger.Debug("Detected " + harmonyId.Value + " from BepInEx"); if (!detectionSet.ContainsKey(harmonyId.Key)) { detectionSet.Add(harmonyId.Key, harmonyId.Value); } } } } public static void AddToUnpatch(string key) { if (detectionSet.ContainsKey(key)) { unpatchMods.Add(key, detectionSet[key]); } } public static void UnpatchMods(Harmony harmony) { Logging logger = Logging.GetLogger(); foreach (KeyValuePair unpatchMod in unpatchMods) { logger.Warning("Not compatible with " + unpatchMod.Value); Harmony.UnpatchID(unpatchMod.Key); detectionSet.Remove(unpatchMod.Key); logger.Warning("Disabled " + unpatchMod.Value); } } public static bool IsModDetected(string key) { return detectionSet.ContainsKey(key); } public static bool IsModNameDetected(string value) { return detectionSet.ContainsValue(value); } public static bool TryGetDetectedModName(string key, out string mod) { return detectionSet.TryGetValue(key, out mod); } public static bool TryGetDetectedModKey(string value, out string key) { key = null; foreach (string key2 in detectionSet.Keys) { if (detectionSet[key2] == value) { key = key2; return true; } } return false; } public static string AddAnonymousPatch(string baseMethodName, PatchType patchType, string modName, string patchMethodName = null) { string text = null; int num = 0; Logging logger = Logging.GetLogger(); foreach (MethodBase item in Harmony.GetAllPatchedMethods().ToList()) { MethodBaseExtensions.HasMethodBody(item); Patches patchInfo = Harmony.GetPatchInfo(item); ReadOnlyCollection readOnlyCollection = patchInfo.Prefixes; switch (patchType) { case PatchType.Postfix: readOnlyCollection = patchInfo.Postfixes; break; case PatchType.Prefix: readOnlyCollection = patchInfo.Prefixes; break; case PatchType.Transpiler: readOnlyCollection = patchInfo.Transpilers; break; case PatchType.Finalizer: readOnlyCollection = patchInfo.Finalizers; break; } foreach (Patch item2 in readOnlyCollection) { if (!item2.owner.StartsWith("harmony-auto") || !(item.Name == baseMethodName)) { continue; } if (patchMethodName != null) { if (item2.PatchMethod.Name == patchMethodName) { num++; text = item2.owner; } } else { num++; text = item2.owner; } } if (num == 1) { detectionSet.Add(text, modName); logger.Info($"Added unique anonymous {baseMethodName} {patchType}: {text} as {modName}"); } else if (num > 1) { text = null; logger.Warning($"Found multiple anonymous {baseMethodName} {patchType} entries. Can't identify correct patch to remove or modify."); } } if (num == 0) { logger.Info("No patch found for " + modName); } return text; } } public class ImageHelper { private readonly string externalFolder; private readonly Logging logger; public ImageHelper(string externalFolder) { this.externalFolder = externalFolder; logger = Logging.GetLogger(); } public Sprite LoadSprite(string name, int width, int height, bool linear = false, float pixelsPerUnit = 100f) { logger.Debug("Reading image and creating sprite " + name); if (TryLoadImage(name, width, height, linear, out var image)) { return LoadSprite(image, pixelsPerUnit); } return null; } public Sprite LoadSprite(Texture2D texture, float pixelsPerUnit = 100f) { return LoadSprite(texture, ((Object)texture).name, ((Texture)texture).width, ((Texture)texture).height, pixelsPerUnit); } public Sprite LoadSprite(Texture2D texture, string name, float width, float height, float pixelsPerUnit = 100f) { //IL_0044: 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) if ((Object)(object)texture == (Object)null) { return null; } if (string.IsNullOrWhiteSpace(name)) { name = ((Object)texture).name; } logger.Debug("Creating sprite " + name + " from existing texture"); Sprite obj = Sprite.Create(texture, new Rect(0f, 0f, width, height), Vector2.zero, pixelsPerUnit); if ((Object)(object)obj == (Object)null) { throw new ApplicationException("Can't create sprite " + name); } ((Object)obj).name = name; return obj; } public bool TryLoadImage(string name, int width, int height, bool linear, out Texture2D image) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown image = null; string text = Path.Combine(externalFolder, name); logger.Debug("Reading external file " + text); if (!File.Exists(text)) { throw new FileNotFoundException("Cannot find image " + name); } byte[] array = File.ReadAllBytes(text); Texture2D val = new Texture2D(width, height, (TextureFormat)4, true, linear); if (!ImageConversion.LoadImage(val, array, false)) { throw new FileLoadException("Can't load image " + name); } image = val; return true; } } internal class InputCycler { private static InputCycler _instance; private bool useMouseWheel; private DelegatedConfigEntry keyAdvance; private DelegatedConfigEntry keyReferse; public static InputCycler Instance { get { if (_instance == null) { _instance = new InputCycler(); } return _instance; } } private InputCycler() { } public void Init(bool useMouseWheel, DelegatedConfigEntry keyAdvance, DelegatedConfigEntry keyReverse = null) { this.useMouseWheel = useMouseWheel; this.keyAdvance = keyAdvance; keyReferse = keyReverse; } } internal static class InputEvaluator { public unsafe static bool IsPressed(KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) object obj = KeyboardShortcut.Empty; if (((object)(*(KeyboardShortcut*)(&shortcut))/*cast due to .constrained prefix*/).Equals(obj)) { return false; } foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { if ((int)modifier != 0 && !Input.GetKey(modifier)) { return false; } } return Input.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey); } public unsafe static bool IsDown(KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) object obj = KeyboardShortcut.Empty; if (((object)(*(KeyboardShortcut*)(&shortcut))/*cast due to .constrained prefix*/).Equals(obj)) { return false; } foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { if ((int)modifier != 0 && !Input.GetKey(modifier)) { return false; } } return Input.GetKey(((KeyboardShortcut)(ref shortcut)).MainKey); } public unsafe static bool IsReleased(KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) object obj = KeyboardShortcut.Empty; if (((object)(*(KeyboardShortcut*)(&shortcut))/*cast due to .constrained prefix*/).Equals(obj)) { return false; } foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { if ((int)modifier != 0 && !Input.GetKeyUp(modifier)) { return false; } } return Input.GetKeyUp(((KeyboardShortcut)(ref shortcut)).MainKey); } } public class IterativeStopwatch : Stopwatch { private double startMillis; private Logging Log = Logging.GetLogger(); public long Iterations { get; private set; } public double IterationMicroseconds { get; private set; } public double TotalElapsedMicroseconds { get; private set; } public double IterationMilliseconds { get; private set; } public double TotalElapsedMilliseconds { get; private set; } public double AverageMicroseconds { get; private set; } public double AverageMilliseconds { get; private set; } public IterativeStopwatch() { Iterations = 0L; } public new void Start() { startMillis = base.Elapsed.TotalMilliseconds; base.Start(); } public new void Stop() { if (base.IsRunning) { base.Stop(); Iterations++; IterationMilliseconds = base.Elapsed.TotalMilliseconds - startMillis; IterationMicroseconds = IterationMilliseconds * 1000.0; TotalElapsedMilliseconds = base.Elapsed.TotalMilliseconds; TotalElapsedMicroseconds = base.Elapsed.TotalMilliseconds * 1000.0; AverageMilliseconds = TotalElapsedMilliseconds / (double)Iterations; AverageMicroseconds = TotalElapsedMicroseconds / (double)Iterations; } } public new void Reset() { startMillis = 0.0; Iterations = 0L; base.Reset(); } public new void Restart() { startMillis = 0.0; Iterations = 0L; base.Restart(); } } public static class JsonHelper { [Serializable] private class JsonWrapper { public T[] Items; } public static T[] FromJson(string json) { return JsonUtility.FromJson>(json).Items; } public static string ToJson(T[] array) { return JsonUtility.ToJson((object)new JsonWrapper { Items = array }); } public static string ToJson(T[] array, bool prettyPrint) { return JsonUtility.ToJson((object)new JsonWrapper { Items = array }, prettyPrint); } } public class Logging { internal enum LogLevels { Critical, Error, Warning, Info, Debug, Trace } private static Logging _instance; public static Logging Instance { get { if (_instance == null) { throw new InvalidOperationException("Logging.Instance was accessed before initialization. Call Logging.Initialize() in your mod's startup code first."); } return _instance; } private set { _instance = value; } } internal LogLevels LogLevel { get; set; } internal string ModName { get; private set; } private Logging(LogLevels level, string name) { LogLevel = level; ModName = name; } internal static Logging Initialize(LogLevels level, string name) { if (_instance != null) { Instance.Warning("Logger is already initialized. Using existing logger"); return _instance; } Instance = new Logging(level, name); return _instance; } internal static Logging GetLogger(LogLevels level, string name) { if (_instance == null) { Initialize(level, name); } return Instance; } internal static Logging GetLogger() { return Instance; } internal void Trace(string msg) { if (LogLevel >= LogLevels.Trace) { Debug.Log((object)Message(msg)); } } internal void Debug(string msg) { if (LogLevel >= LogLevels.Debug) { Debug.Log((object)Message(msg)); } } internal void Info(string msg) { if (LogLevel >= LogLevels.Info) { Debug.Log((object)Message(msg)); } } internal void Warning(string msg) { if (LogLevel >= LogLevels.Warning) { Debug.LogWarning((object)Message(msg)); } } internal void Error(string msg) { if (LogLevel >= LogLevels.Error) { Debug.LogWarning((object)Message(msg)); } } internal void Error(Exception e) { Error(e, stackTrace: false); } internal void Error(Exception e, bool stackTrace) { if (LogLevel >= LogLevels.Error) { Warning(Message(e.Message)); if (stackTrace) { Warning(e.StackTrace); } } } internal void Critical(Exception e) { if (LogLevel >= LogLevels.Critical) { Debug(Message(e.Message)); Error(e.StackTrace); } } private string Message(string msg) { return ModName + ": " + msg; } internal bool Testing() { return Instance.LogLevel >= LogLevels.Debug; } internal static void ChangeLogging(object s, EventArgs e) { ConfigEntry val = s as ConfigEntry; if (_instance != null) { Instance.LogLevel = val.Value; Instance.Info($"Change {((ConfigEntryBase)val).Definition.Key} to {val.Value}"); } } } public static class ModResourceBootstrap { public static string InitializeModResources(Type pluginType, string modName) { string orCreateModConfigFolder = ConfigFolderHelper.GetOrCreateModConfigFolder(modName); Logging.Instance.Debug("Mod config folder: " + orCreateModConfigFolder); string prefix = modName + ".Resources."; Logging.Instance.Debug("Mod resources: " + orCreateModConfigFolder); ExtractResources(pluginType.Assembly, prefix, orCreateModConfigFolder); return orCreateModConfigFolder; } private static void ExtractResources(Assembly asm, string prefix, string targetFolder) { Logging.Instance.Debug("Checking resources for " + prefix); string[] manifestResourceNames = asm.GetManifestResourceNames(); foreach (string text in manifestResourceNames) { Logging.Instance.Debug("Resource " + text); if (!text.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { continue; } string text2 = text.Substring(prefix.Length); string text3 = Path.Combine(targetFolder, text2); Logging.Instance.Debug("Extracting " + text2 + " to " + text3); if (File.Exists(text3)) { continue; } using Stream stream = asm.GetManifestResourceStream(text); using FileStream destination = File.Create(text3); stream.CopyTo(destination); } } } public class ServerConfiguration { [HarmonyPatch(typeof(ZNet), "StopAll")] private static class ZNet_Shutdown_Patch { [HarmonyPrefix] private static void ZNet_StopAll_Prefix(ZNet __instance) { if (_instance != null) { Log.Debug("ZNet_StopAll_Patch_Prefix"); _instance.ReceivedServerValues = false; } } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class ZNet_OnNewConnection_Patch { private static void Postfix(ZNet __instance, ZNetPeer peer) { Log.Debug("ZNet OnNewConnection postfix"); if (!__instance.IsServer()) { try { peer.m_rpc.Register("ClientConfigReceiver." + GetPluginGuid(), (Action)Instance.RPC_ClientConfigReceiver); Log.Debug("Player registered RPC_ClientConfigReceiver"); return; } catch (Exception) { Log.Warning("Failed to register RPC"); return; } } try { Instance.SendConfigToClient(peer); } catch (Exception) { Log.Warning("Error sending server configuration to client"); } } } public static List ServerDelegatedEntries = new List(); private static ConfigFile LocalConfig; private static BaseUnityPlugin Mod; private static string ConfigFileName; private static ServerConfiguration _instance; private static Logging Log; public bool IsSetup; public bool ReceivedServerValues; public FileSystemWatcher ConfigWatcher; private const string NOT_CONFIGURED = "ServerConfiguration not initialized. Setup first."; public static ServerConfiguration Instance { get { if (_instance == null) { _instance = new ServerConfiguration(); } return _instance; } } private ServerConfiguration() { } public void Setup(ConfigFile config, BaseUnityPlugin modInstance) { LocalConfig = config; Log = Logging.GetLogger(); Log.Trace("ServerConfiguration Setup"); Mod = modInstance; ConfigFileName = Path.GetFileName(LocalConfig.ConfigFilePath); IsSetup = true; } public void CreateConfigWatcher() { ConfigWatcher = Utils.CreateFileWatcher(LocalConfig.ConfigFilePath, LoadConfig); } private void LoadConfig(object sender, FileSystemEventArgs e) { if (!File.Exists(LocalConfig.ConfigFilePath)) { return; } try { Log.Debug($"Loading configuration {e.ChangeType}"); LocalConfig.Reload(); } catch { Log.Error("Error loading configuration file " + ConfigFileName); } } public static string GetPluginGuid() { return Mod.Info.Metadata.GUID; } public void RPC_ClientConfigReceiver(ZRpc zrpc, ZPackage package) { if (!Instance.IsSetup) { Log.Error("ServerConfiguration not initialized. Setup first."); return; } Log.Debug("ClientConfigReceiver"); string section; string name; while (package.GetPos() < package.Size()) { section = package.ReadString(); name = package.ReadString(); string text = package.ReadString(); Log.Trace("Reading " + section + " " + name + " value " + text + " from ZPackage"); DelegatedConfigEntryBase delegatedConfigEntryBase = ServerDelegatedEntries.Find((DelegatedConfigEntryBase e) => e.Name == name && e.Section == section); if (delegatedConfigEntryBase != null) { Log.Trace("Found DCEB on client and setting to server value " + text); delegatedConfigEntryBase.ServerValue = text; } ConfigEntryBase val = LocalConfig[section, name]; if (val != null) { Log.Trace("Found local CEB and setting underlying config value " + text); val.SetSerializedValue(text); } } ReceivedServerValues = true; } internal void WriteConfigEntries(ZPackage zpkg) { foreach (DelegatedConfigEntryBase serverDelegatedEntry in ServerDelegatedEntries) { Log.Trace("Writing " + serverDelegatedEntry.Section + " " + serverDelegatedEntry.Name + " value " + serverDelegatedEntry.ServerValue + " to ZPackage"); zpkg.Write(serverDelegatedEntry.Section); zpkg.Write(serverDelegatedEntry.Name); zpkg.Write(serverDelegatedEntry.ServerValue); } } internal void SendConfigToClient(ZNetPeer peer) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown if (!Instance.IsSetup) { Log.Error("ServerConfiguration not initialized. Setup first."); return; } Log.Debug("SendConfigToClient"); ZPackage val = new ZPackage(); WriteConfigEntries(val); peer.m_rpc.Invoke("ClientConfigReceiver." + GetPluginGuid(), new object[1] { val }); Log.Trace("Invoked ClientConfigReceiver on peer"); } public void SendConfigToAllClients(object o, SettingChangedEventArgs e) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown if (!IsSetup) { Log.Error("ServerConfiguration not initialized. Setup first."); } else if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ZNet.instance.GetPeerConnections() > 0) { Log.Debug("SendConfigToAllClients"); ZPackage zpkg = new ZPackage(); WriteConfigEntries(zpkg); ((MonoBehaviour)Mod).StartCoroutine(_instance.Co_BroadcastConfig(zpkg)); } } private IEnumerator Co_BroadcastConfig(ZPackage zpkg) { Log.Debug("Co_BroadcastConfig"); List connectedPeers = ZNet.instance.GetConnectedPeers(); foreach (ZNetPeer item in connectedPeers) { if (item != ZNet.instance.GetServerPeer()) { item.m_rpc.Invoke("ClientConfigReceiver." + GetPluginGuid(), new object[1] { zpkg }); Log.Trace("Invoked ClientConfigReceiver on peer"); } yield return null; } } } public class Utils { public static TEnum Guardrails(string value, TEnum enumDefault) where TEnum : struct { if (Enum.TryParse(value, ignoreCase: true, out var result)) { return result; } return enumDefault; } public static int Guardrails(int value, int lbound, int ubound) { if (value < lbound) { return lbound; } if (value > ubound) { return ubound; } return value; } public static string Truncate(string value, int maxChars) { if (value == null) { return null; } if (value.Length <= maxChars) { return value; } return value.Substring(0, maxChars); } public static void GetCharactersInRangeXZ(Vector3 point, float radius, List characters) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) float num = radius * radius; foreach (Character s_character in Character.s_characters) { if (DistanceSqrXZ(((Component)s_character).transform.position, point) < num) { characters.Add(s_character); } } } public static PinData GetClosestPin(Vector3 pos, float radius, PinType pType) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) PinData result = null; float num = float.MaxValue; float num2 = radius * radius; if ((Object)(object)Minimap.instance != (Object)null) { foreach (PinData pin in Minimap.instance.m_pins) { if ((Object)(object)pin.m_uiElement != (Object)null && pin.m_type == pType) { float num3 = DistanceSqrXZ(pos, pin.m_pos); if (Logging.Instance.LogLevel >= Logging.LogLevels.Debug) { Logging.Instance.Debug($"Testing for pin at {pin.m_pos} within radius {radius} from {pos}"); } if (num3 < num2 && num3 < num) { Logging.Instance.Debug("Pin is within radius"); num = num3; result = pin; } } } } return result; } public static float DistanceSqr(Vector3 v0, Vector3 v1) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) Vector3 val = v1 - v0; return ((Vector3)(ref val)).sqrMagnitude; } public static float DistanceSqrXZ(Vector3 v0, Vector3 v1) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) float num = v1.x - v0.x; float num2 = v1.z - v0.z; return num * num + num2 * num2; } public static Vector3 ClosestPoint(Vector3 point, IEnumerable collection, Func selector) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) Vector3 result = default(Vector3); float num = float.MaxValue; foreach (T item in collection) { Vector3 val = selector(item); Vector3 val2 = val - point; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; result = val; } } return result; } public static T ClosestObject(Vector3 point, IEnumerable collection, Func selector) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) T result = default(T); float num = float.MaxValue; foreach (T item in collection) { Vector3 val = selector(item) - point; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; result = item; } } return result; } public static Vector3 ClosestPointXZ(Vector3 point, IEnumerable collection, Func selector) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) Vector3 result = default(Vector3); float num = float.MaxValue; foreach (T item in collection) { Vector3 val = selector(item); float num2 = DistanceSqrXZ(val, point); if (num2 < num) { num = num2; result = val; } } return result; } public static T ClosestObjectXZ(Vector3 point, IEnumerable collection, Func selector) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) T result = default(T); float num = float.MaxValue; foreach (T item in collection) { float num2 = DistanceSqrXZ(selector(item), point); if (num2 < num) { num = num2; result = item; } } return result; } public static float Guardrails(float value, float lbound, float ubound) { if (value < lbound) { return lbound; } if (value > ubound) { return ubound; } return value; } public static string UnClonifiedName(string name) { if (name == null) { return null; } int num = name.IndexOf("(Clone)"); if (num < 1) { return name; } return name.Substring(0, num); } public static void SetTranslator(int id, string idText) { Localization.instance.AddWord("skill_" + id, idText); } public static string GetTranslated(int id) { Logging.GetLogger().Debug(string.Format("Got translation for id {0} to {1}", id, Localization.instance.Localize("skill_" + id))); return Localization.instance.Localize("$skill_" + id); } public static string GetAssemblyPathedFile(string fileName) { return new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName.Replace('\\', '/') + "/" + fileName; } public static Sprite GetPrefabIcon(string prefabName) { Sprite result = null; GameObject prefab = GetPrefab(prefabName); ItemDrop val = default(ItemDrop); if (Object.op_Implicit((Object)(object)prefab) && prefab.TryGetComponent(ref val)) { result = val.m_itemData.GetIcon(); } return result; } public static Player GetPlayerByZDOID(ZDOID zid) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) foreach (Player allPlayer in Player.GetAllPlayers()) { ZDOID zDOID = ((Character)allPlayer).GetZDOID(); if (((ZDOID)(ref zDOID)).Equals(zid)) { return allPlayer; } } return null; } public static Character GetCharacterByZDOID(string cid) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) foreach (Character allCharacter in Character.GetAllCharacters()) { if (((object)allCharacter.GetZDOID()/*cast due to .constrained prefix*/).ToString().Equals(cid)) { return allCharacter; } } return null; } public static Character GetCharacterByZDOID(ZDOID cid) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) foreach (Character allCharacter in Character.GetAllCharacters()) { ZDOID zDOID = allCharacter.GetZDOID(); if (((ZDOID)(ref zDOID)).Equals(cid)) { return allCharacter; } } return null; } public static ZNetPeer GetPeerByRPC(ZRpc rpc) { foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer.m_rpc == rpc) { return peer; } } return null; } public static List GetGameObjectsOfType(Type t) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) List list = new List(); Object[] array = Object.FindObjectsByType(t, (FindObjectsSortMode)0); foreach (Object val in array) { list.Add(((Component)val).gameObject); } return list; } public static GameObject GetClosestGameObjectOfType(Type t, Vector3 point, float radius) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return GetGameObjectsOfTypeInRangeByDistance(t, point, radius)?[0]; } public static List GetGameObjectsOfTypeInRangeByDistance(Type t, Vector3 point, float radius) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) List> list = new List>(); List gameObjectsOfTypeInRange = GetGameObjectsOfTypeInRange(t, point, radius); if (gameObjectsOfTypeInRange.Count > 0) { foreach (GameObject item in gameObjectsOfTypeInRange) { list.Add(new KeyValuePair(item, Vector3.Distance(item.transform.position, point))); } list.Sort((KeyValuePair pair1, KeyValuePair pair2) => pair1.Value.CompareTo(pair2.Value)); return list.ConvertAll((KeyValuePair x) => x.Key); } return null; } public static List GetGameObjectsOfTypeInRange(Type t, Vector3 point, float radius) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) float radiusSq = radius * radius; return GetGameObjectsOfType(t).Where(delegate(GameObject x) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vector3 val = x.transform.position - point; return ((Vector3)(ref val)).sqrMagnitude < radiusSq; }).ToList(); } public static float GetPointDepth(Vector3 p) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) return ZoneSystem.instance.m_waterLevel - GetSolidHeight(p); } public static List GetDelimitedStringAsList(string delimitedString, char delimiter) { List list = new List(); string[] array = delimitedString.Split(new char[1] { delimiter }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { list.Add(text.Trim()); } return list; } public static float GetSolidHeight(Vector3 p) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) int solidRayMask = ZoneSystem.instance.m_solidRayMask; float result = 0f; p.y += 1000f; RaycastHit val = default(RaycastHit); if (Physics.Raycast(p, Vector3.down, ref val, 2000f, solidRayMask) && !Object.op_Implicit((Object)(object)((RaycastHit)(ref val)).collider.attachedRigidbody)) { result = ((RaycastHit)(ref val)).point.y; } return result; } public static Transform FindChild(Transform aParent, string aName) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown foreach (Transform item in aParent) { Transform val = item; if (((Object)val).name == aName) { return val; } Transform val2 = FindChild(val, aName); if ((Object)(object)val2 != (Object)null) { return val2; } } return null; } public static Transform FindParent(Transform go) { while ((Object)(object)go.parent != (Object)null) { go = go.parent; } return go; } public static bool IsPrefabInScene(string prefabName) { return IsPrefabInScene(StringExtensionMethods.GetStableHashCode(prefabName)); } public static bool IsPrefabInScene(int prefabHash) { ZNetScene instance = ZNetScene.instance; return (Object)(object)((instance != null) ? instance.GetPrefab(prefabHash) : null) != (Object)null; } public static bool TryGetPrefab(string prefabName, out GameObject prefab) { return TryGetPrefab(StringExtensionMethods.GetStableHashCode(prefabName), out prefab); } public static bool TryGetPrefab(int prefabHash, out GameObject prefab) { prefab = GetPrefabByHash(prefabHash); return (Object)(object)prefab != (Object)null; } public static GameObject GetPrefabByHash(int prefabHash) { GameObject val = null; Logging logger = Logging.GetLogger(); ZNetScene instance = ZNetScene.instance; val = ((instance != null) ? instance.GetPrefab(prefabHash) : null); if ((Object)(object)val != (Object)null) { logger.Debug("Found prefab in Scene"); return val; } val = ObjectDB.instance.GetItemPrefab(prefabHash); if ((Object)(object)val != (Object)null) { logger.Debug("Found prefab in ObjectDB"); return val; } logger.Debug("No prefab found"); return val; } public static GameObject GetPrefab(int prefabHash) { return GetPrefabByHash(prefabHash); } public static GameObject GetPrefab(string prefabName) { return GetPrefabByHash(StringExtensionMethods.GetStableHashCode(prefabName)); } public static void LoadPrefabInScene(GameObject prefab) { int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)prefab).name); if ((Object)(object)ZNetScene.instance != (Object)null && !ZNetScene.instance.m_namedPrefabs.ContainsKey(stableHashCode)) { ZNetScene.instance.m_prefabs.Add(prefab); ZNetScene.instance.m_namedPrefabs[stableHashCode] = prefab; } } public static GameObject SpawnObject(string prefabName, Vector3 position, Quaternion rotation, bool persist = true) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (TryGetPrefab(prefabName, out var prefab)) { return SpawnObject(prefab, position, rotation, persist); } return null; } public static GameObject SpawnObject(GameObject prefab, Vector3 position, Quaternion rotation, bool persist = true) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) LoadPrefabInScene(prefab); GameObject val = Object.Instantiate(prefab, position, rotation); ZNetView val2 = default(ZNetView); if (persist && val.TryGetComponent(ref val2)) { val2.GetZDO().Persistent = true; } return val; } public static string SerializeFromDictionary(string delimp, string delimc, IDictionary dict) { if (dict == null) { return null; } IEnumerable values = dict.Select(delegate(KeyValuePair kvp) { KeyValuePair keyValuePair = kvp; string? obj = keyValuePair.Key?.ToString(); string text = delimc; keyValuePair = kvp; return obj + text + keyValuePair.Value; }); return string.Join(delimp, values); } public static void DeserializeToDictionary(string serializedString, string delimp, string delimc, ref IDictionary dict) { if (dict == null) { return; } dict.Clear(); string[] separator = new string[1] { delimp }; string[] separator2 = new string[1] { delimc }; string[] array = serializedString.Split(separator, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(separator2, StringSplitOptions.RemoveEmptyEntries); if (array2.Length == 2) { dict.Add(TypedValue(array2[0]), TypedValue(array2[1])); } } } public static FileSystemWatcher CreateFileWatcher(string fullPath, FileSystemEventHandler handler) { string fileName = Path.GetFileName(fullPath); FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(fullPath.Substring(0, fullPath.Length - fileName.Length), fileName); fileSystemWatcher.NotifyFilter = NotifyFilters.Attributes | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime; fileSystemWatcher.Changed += handler; fileSystemWatcher.Created += handler; fileSystemWatcher.IncludeSubdirectories = false; fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; fileSystemWatcher.EnableRaisingEvents = true; return fileSystemWatcher; } public static T TypedValue(object a) { return (T)Convert.ChangeType(a, typeof(T)); } public static float TimeAdjustedRamp(float maxValue, float duration, float elapsedTime, float pctFromStartRise, float pctFromEndFall) { float num = elapsedTime / duration; if (num <= pctFromStartRise) { return maxValue * (num / pctFromStartRise); } if (num >= 1f - pctFromEndFall) { return maxValue * ((1f - num) / pctFromEndFall); } return maxValue; } public static Color IntToColor(int rgba) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) float num = (float)((rgba >> 16) & 0xFF) / 255f; float num2 = (float)((rgba >> 8) & 0xFF) / 255f; float num3 = (float)(rgba & 0xFF) / 255f; float num4 = (float)((rgba >> 24) & 0xFF) / 255f; return new Color(num, num2, num3, num4); } public static bool CopyComponentToGameObject(Component original, ref GameObject destination) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown Logging logger = Logging.GetLogger(); Type type = ((object)original).GetType(); logger.Debug($"Original Type is {type}"); GameObject obj = destination; logger.Debug("Destination GameObject " + ((obj != null) ? ((Object)obj).name : null)); Component val = destination.GetComponent(type); if (!Object.op_Implicit((Object)(object)val)) { val = destination.AddComponent(type); } if (!Object.op_Implicit((Object)(object)val)) { logger.Debug("Destination component is null"); return false; } Component val2 = (Component)Activator.CreateInstance(type); if (!Object.op_Implicit((Object)(object)val2)) { logger.Debug("Destination component is null"); return false; } if (!Object.op_Implicit((Object)(object)val2)) { logger.Debug("Boxed component is null"); return false; } FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { fieldInfo.SetValue(val2, fieldInfo.GetValue(original)); } val = val2; return true; } public static bool CopyObject(object original, object target) { Logging logger = Logging.GetLogger(); Type type = original.GetType(); Type type2 = target.GetType(); if (type == null) { logger.Warning("Copy Object: Source object is null"); return false; } if (type2 == null) { logger.Warning("Copy Object: Destination object is null"); return false; } if (type2 != type) { logger.Warning("Copy Object: Source and destination components are different types"); return false; } FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { fieldInfo.SetValue(target, fieldInfo.GetValue(original)); } return true; } } } namespace neobotics.ValheimMods { internal class Cfg { public static DelegatedConfigEntry guardAttacks; public static DelegatedConfigEntry debugLevel; public static DelegatedConfigEntry biomeRestriction; public static DelegatedConfigEntry bossProgression; public static DelegatedConfigEntry piecesNeeded; public static DelegatedConfigEntry proximityCheckRadius; public static DelegatedConfigEntry stationsNeeded; public static DelegatedConfigEntry bedsNeeded; public static DelegatedConfigEntry creatureBiomeMapping; public static DelegatedConfigEntry allowLevels; public static void BepInExConfig(BaseUnityPlugin _instance) { //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Expected O, but got Unknown ServerConfiguration.Instance.Setup(_instance.Config, _instance); debugLevel = new DelegatedConfigEntry(ChangeLogging); debugLevel.ConfigEntry = _instance.Config.Bind("Utility", "LogLevel", Logging.LogLevels.Info, "Controls the level of information contained in the log"); Logging.GetLogger().LogLevel = debugLevel.Value; biomeRestriction = new DelegatedConfigEntry(useServerDelegate: true); biomeRestriction.ConfigEntry = _instance.Config.Bind("Restrictions", "Limit by biome", false, "If enabled, restricts summoned creatures to those that can spawn in the selected ward's biome, or 'higher' biomes, e.g. a Troll can be summoned to a ward in the Plains, but a Seeker cannot.@"); bossProgression = new DelegatedConfigEntry(useServerDelegate: true); bossProgression.ConfigEntry = _instance.Config.Bind("Restrictions", "Limit by Progression", false, "If enabled, allows creatures to be summoned in 'lower' biomes once a boss is defeated, e.g. Once Bonemass is defeated, a Draugr can be summoned to a ward in the Meadows, Black Forest and Swamp.@"); piecesNeeded = new DelegatedConfigEntry(useServerDelegate: true); piecesNeeded.ConfigEntry = _instance.Config.Bind("Restrictions", "Minimum Base Pieces", 100, "The minimum number of build pieces that must be in proximity to the ward to be considered a 'base' (based on the Piece Check Radius)@"); proximityCheckRadius = new DelegatedConfigEntry(useServerDelegate: true); proximityCheckRadius.ConfigEntry = _instance.Config.Bind("Restrictions", "Base Check Radius", 50, "The radius from the ward to check for a 'base' that allows the ward to be used for summoning a guard.@"); stationsNeeded = new DelegatedConfigEntry(useServerDelegate: true); stationsNeeded.ConfigEntry = _instance.Config.Bind("Restrictions", "Minimum Base Crafting Stations", 1, "The minimum number of crafting stations that must be in proximity to the ward to be considered a 'base' (based on the Piece Check Radius)@"); bedsNeeded = new DelegatedConfigEntry(useServerDelegate: true); bedsNeeded.ConfigEntry = _instance.Config.Bind("Restrictions", "Minimum Base Beds", 1, "The minmium number of beds that must be in proximity to the ward to be considered a 'base' (based on the Piece Check Radius)@"); allowLevels = new DelegatedConfigEntry(useServerDelegate: true); allowLevels.ConfigEntry = _instance.Config.Bind("Restrictions", "Enable Guardian Levels", true, "If enabled, allows trophies from 'starred' creatures to summon guardians of the same level, i.e. a trophy from a 2-star will summon a 2-star guardian.@"); creatureBiomeMapping = new DelegatedConfigEntry(DefendYourBase.ChangeBiomeMapping, useServerDelegate: true); creatureBiomeMapping.ConfigEntry = _instance.Config.Bind("Hidden", "Creature Biome Mapping", DefendYourBase.creatureConfigString, new ConfigDescription("The list of creature-to-biome mappings. Used by server authoritative configs only. Edit the CreatureBiomeMap.txt file to change local mapping.@", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Browsable = false } })); ServerConfiguration.Instance.CreateConfigWatcher(); } public static void ChangeLogging(object s, EventArgs e) { Logging logger = Logging.GetLogger(); SettingChangedEventArgs e2 = (SettingChangedEventArgs)(object)((e is SettingChangedEventArgs) ? e : null); logger.Debug($"ChangeLog {e2.ChangedSetting.Definition.Key} to {e2.ChangedSetting.BoxedValue}"); logger.LogLevel = debugLevel.Value; } } internal class CreatureInfo { public string Name { get; } public bool Tamed { get; } public bool Tapped { get; } public bool Boss { get; } public int Level { get; } public bool DropsEnabled { get; } public CreatureInfo(string name, bool tamed, bool tapped, bool boss, int level, bool dropsEnabled) { Name = name; Tamed = tamed; Tapped = tapped; Boss = boss; Level = level; DropsEnabled = dropsEnabled; } } [BepInPlugin("neobotics.valheim_mod.defendyourbase", "DefendYourBase", "0.2.6")] [BepInProcess("valheim.exe")] [BepInProcess("valheim_server.exe")] public class DefendYourBase : BaseUnityPlugin { [HarmonyPatch(typeof(Character), "RaiseSkill")] private static class Character_RaiseSkill_Patch { [HarmonyPrefix] private static bool Character_RaiseSkill_Prefix(Character __instance) { Log.Debug("Character_RaiseSkill_Patch_Prefix"); Vector3 val = default(Vector3); if (__instance.m_nview.GetZDO().GetVec3(GUARD_PATROL_POINT, ref val)) { return false; } return true; } } [HarmonyPatch(typeof(ZNetScene), "Awake")] private static class ZNetScene_Awake_Patch { [HarmonyPostfix] private static void ZNetScene_Awake_Postfix(ZNetScene __instance) { Log.Debug("ZNetScene_Awake_Patch_Postfix"); BuildTrophyCreatureMap(); } } [HarmonyPatch(typeof(PrivateArea), "UseItem")] private static class PrivateArea_UseItem_Patch { [HarmonyPostfix] private static void PrivateArea_UseItem_Postfix(PrivateArea __instance, Humanoid user, ItemData item, ref bool __result) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_02c7: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_03b1: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Unknown result type (might be due to invalid IL or missing references) //IL_03c6: Unknown result type (might be due to invalid IL or missing references) //IL_03cb: Unknown result type (might be due to invalid IL or missing references) //IL_03d0: Unknown result type (might be due to invalid IL or missing references) //IL_03d4: Unknown result type (might be due to invalid IL or missing references) //IL_03d6: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0341: Unknown result type (might be due to invalid IL or missing references) //IL_0346: 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_0440: Unknown result type (might be due to invalid IL or missing references) //IL_0451: Unknown result type (might be due to invalid IL or missing references) //IL_04e1: Unknown result type (might be due to invalid IL or missing references) //IL_04e6: 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_0513: Unknown result type (might be due to invalid IL or missing references) //IL_04a0: Unknown result type (might be due to invalid IL or missing references) Log.Debug("PrivateArea_UseItem_Patch_Postfix"); if (!((Character)user).IsPlayer()) { return; } if ((int)item.m_shared.m_itemType != 13) { Log.Debug(item.m_shared.m_name + " is not a trophy"); return; } __result = true; int quality = item.m_quality; if (!trophyCreatureMap.TryGetValue(item.m_shared.m_name, out var value)) { Log.Debug("Trophy not mapped"); ((Character)Player.m_localPlayer).Message((MessageType)2, "$msg_neobotics_dyb_cant_summon", 0, (Sprite)null); return; } Player val = (Player)(object)((user is Player) ? user : null); if (__instance.GetCreatorName() != val.GetPlayerName()) { Log.Debug("Can't summon on someone else's ward"); ((Character)Player.m_localPlayer).Message((MessageType)2, "$msg_neobotics_dyb_others_ward", 0, (Sprite)null); return; } if (__instance.m_nview.GetZDO().GetBool(WARD_FLAG, false)) { Log.Debug("Ward already has a guard"); ((Character)Player.m_localPlayer).Message((MessageType)2, "$msg_neobotics_dyb_has_guard", 0, (Sprite)null); return; } if ((Cfg.piecesNeeded.Value > 0 || Cfg.stationsNeeded.Value > 0) && Cfg.proximityCheckRadius.Value > 0 && !CheckPiecesInRadius(((Component)__instance).transform.position, Cfg.proximityCheckRadius.Value, Cfg.piecesNeeded.Value, Cfg.stationsNeeded.Value, Cfg.bedsNeeded.Value)) { Log.Debug("Ward is not close enough to a large enough base"); ((Character)Player.m_localPlayer).Message((MessageType)2, "$msg_neobotics_dyb_not_close", 0, (Sprite)null); return; } List list = new List(); if (GetAllGuardedWardZDOs(list)) { foreach (ZDO item2 in list) { if (Vector3.Distance(item2.m_position, ((Component)__instance).transform.position) < __instance.m_radius) { Log.Debug("A guarded ward is within selected ward's radius of protection"); ((Character)Player.m_localPlayer).Message((MessageType)2, "$msg_neobotics_dyb_one_guard", 0, (Sprite)null); return; } } } GameObject prefabByHash = Utils.GetPrefabByHash(value); string empty = string.Empty; if ((Object)(object)prefabByHash == (Object)null) { Log.Warning("Can't find creature prefab for trophy."); return; } MonsterAI val2 = default(MonsterAI); if (!prefabByHash.TryGetComponent(ref val2)) { Log.Debug("Creature has no MonsterAI"); ((Character)Player.m_localPlayer).Message((MessageType)2, "$msg_neobotics_dyb_cant_summon", 0, (Sprite)null); return; } Character val3 = default(Character); if (!prefabByHash.TryGetComponent(ref val3)) { Log.Warning("Creature has no Character"); ((Character)Player.m_localPlayer).Message((MessageType)2, "$msg_neobotics_dyb_cant_summon", 0, (Sprite)null); return; } if (val3.m_boss) { Log.Debug("Can't summon a boss"); ((Character)Player.m_localPlayer).Message((MessageType)2, "$msg_neobotics_dyb_no_boss", 0, (Sprite)null); return; } bool flag = true; bool flag2 = false; if (Cfg.bossProgression.Value) { flag2 = CheckBossLevel(((Component)__instance).transform.position, item.m_shared.m_name); flag = false; } if (Cfg.biomeRestriction.Value) { flag = CheckGuardBiome(((Component)__instance).transform.position, item.m_shared.m_name); } if (!(flag || flag2)) { Log.Debug($"Guard not valid for biome {flag} or boss {flag2}"); string text = ((object)Heightmap.FindBiome(((Component)__instance).transform.position)/*cast due to .constrained prefix*/).ToString().ToLower(); string arg = Localization.instance.Localize("$biome_" + text); string text2 = string.Format(Localization.instance.Localize("$msg_neobotics_dyb_guard_biome"), arg); ((Character)Player.m_localPlayer).Message((MessageType)2, text2, 0, (Sprite)null); return; } Log.Debug("Summoning Guard"); Vector3 position = ((Component)__instance).transform.position + ((Component)__instance).transform.forward * 2f; GameObject val4 = Utils.SpawnObject(prefabByHash, position, Quaternion.identity); Character val5 = default(Character); if (!((Object)(object)val4 != (Object)null) || !val4.TryGetComponent(ref val5)) { return; } ConsumeItem(user, item); empty = val5.m_name; ZNetView nview = val5.m_nview; if ((Object)(object)nview != (Object)null && nview.IsValid()) { ZDO zDO = nview.GetZDO(); zDO.Set(GUARD_PATROL_POINT, ((Component)__instance).transform.position); ZDOMan.instance.ForceSendZDO(zDO.m_uid); } if ((Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.IsValid()) { ZDO zDO2 = __instance.m_nview.GetZDO(); if (zDO2.IsOwner()) { zDO2.Set(WARD_FLAG, true); ZDOMan.instance.ForceSendZDO(zDO2.m_uid); } } SetUpGuard(val4, __instance.m_radius, ((Component)__instance).transform.position, quality); GameObject prefab = Utils.GetPrefab("vfx_spawn"); if (Object.op_Implicit((Object)(object)prefab)) { Object.Instantiate(prefab, ((Component)__instance).transform.position, Quaternion.identity); } GameObject prefab2 = Utils.GetPrefab("sfx_offering"); if (Object.op_Implicit((Object)(object)prefab2)) { Object.Instantiate(prefab2, ((Component)__instance).transform.position, Quaternion.identity); } string arg2 = Localization.instance.Localize(empty); string text3 = string.Format(Localization.instance.Localize("$msg_neobotics_dyb_summoned"), arg2); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, text3, 0, (Sprite)null); } } } [HarmonyPatch(typeof(Character), "OnDeath")] private static class Character_OnDeath_Patch { [HarmonyPrefix] private static void Character_OnDeath_Prefix(Character __instance) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) Log.Debug("Character_OnDeath_Patch_Prefix"); Vector3 position = default(Vector3); if (!__instance.m_nview.GetZDO().GetVec3(GUARD_PATROL_POINT, ref position)) { return; } Log.Debug("Guardian died"); List list = new List(); if (GetAllGuardedWardZDOs(list)) { ZDO val = list.Find((ZDO x) => x.m_position == position); if (val != null) { Log.Debug("Guardian ward found"); val.Set(WARD_FLAG, false); ZDOMan.instance.ForceSendZDO(val.m_uid); } } } } [HarmonyPatch(typeof(BaseAI), "IsEnemy", new Type[] { typeof(Character), typeof(Character) })] private static class BaseAI_IsEnemy_Patch { [HarmonyPostfix] private static void BaseAI_IsEnemy_Postfix(BaseAI __instance, Character a, Character b, ref bool __result) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)a == (Object)null) && !((Object)(object)b == (Object)null)) { object obj; if (a == null) { obj = null; } else { ZNetView nview = a.m_nview; obj = ((nview != null) ? nview.GetZDO() : null); } ZDO val = (ZDO)obj; object obj2; if (b == null) { obj2 = null; } else { ZNetView nview2 = b.m_nview; obj2 = ((nview2 != null) ? nview2.GetZDO() : null); } ZDO val2 = (ZDO)obj2; Vector3 val3 = default(Vector3); if (val != null && val2 != null && ((val.GetVec3(WARD_FLAG, ref val3) && !b.m_tamed) || (val2.GetVec3(WARD_FLAG, ref val3) && !a.m_tamed))) { __result = true; } } } } [HarmonyPatch(typeof(MonsterAI), "UpdateTarget")] private static class MonsterAI_UpdateTarget_Patch { [HarmonyPostfix] private static void MonsterAI_UpdateTarget_Postfix(MonsterAI __instance, Humanoid humanoid, float dt) { ZDO val = ((BaseAI)(__instance?)).m_nview?.m_zdo; Vector3 val2 = default(Vector3); if (val != null && val.GetVec3(GUARD_PATROL_POINT, ref val2) && ((BaseAI)__instance).IsAlerted() && (Object)(object)__instance.m_targetCreature == (Object)null && __instance.m_timeSinceSensedTargetCreature > 1f) { ((BaseAI)__instance).SetAlerted(false); __instance.m_targetCreature = null; __instance.m_targetStatic = null; __instance.m_timeSinceAttacking = 0f; __instance.m_updateTargetTimer = 0f; } } } [HarmonyPatch(typeof(BaseAI), "ResetRandomMovement")] private static class BaseAI_ResetRandomMovement_Patch { [HarmonyPrefix] private static void BaseAI_ResetRandomMovement_Prefix(BaseAI __instance) { Log.Debug("BaseAI_ResetRandomMovement_Patch_Prefix"); } [HarmonyPostfix] private static void BaseAI_ResetRandomMovement_Postfix(BaseAI __instance) { Log.Debug("BaseAI_ResetRandomMovement_Patch_Postfix"); } } [HarmonyPatch(typeof(WearNTear), "Destroy")] private static class WearNTear_Destroy_Patch { [HarmonyPrefix] private static void WearNTear_Destroy_Prefix(WearNTear __instance) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) Log.Debug("WearNTear_Destroy_Patch_Prefix"); if (!__instance.m_nview.GetZDO().GetBool(WARD_FLAG, false)) { return; } List list = new List(); if (!TryGetGuardZDOs(list)) { return; } foreach (ZDO item in list) { if (item.GetVec3(GUARD_PATROL_POINT, Vector3.zero) == ((Component)__instance).gameObject.transform.position) { Object.Instantiate(Utils.GetPrefab("vfx_odin_despawn"), item.m_position, Quaternion.identity); ZNetView val = ZNetScene.instance.FindInstance(item); if ((Object)(object)val != (Object)null) { val.Destroy(); } break; } } } } [HarmonyPatch(typeof(ZNetScene), "CreateObject")] private static class ZNetScene_CreateObject_Patch { [HarmonyPostfix] private static void ZNetScene_CreateObject_Postfix(ZNetScene __instance, ZDO zdo, ref GameObject __result) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) Vector3 patrolPoint = default(Vector3); if ((Object)(object)__result == (Object)null || !zdo.GetVec3(GUARD_PATROL_POINT, ref patrolPoint)) { return; } Log.Debug("Creating tamed Guard"); if (privateAreaRadius == 0f) { privateAreaRadius = 32f; GameObject prefab = Utils.GetPrefab("guard_stone"); if ((Object)(object)prefab != (Object)null) { PrivateArea val = default(PrivateArea); if (prefab.TryGetComponent(ref val)) { privateAreaRadius = val.m_radius; Log.Debug($"Setting Ward radius to {privateAreaRadius}"); } else { Log.Debug("Can't get Ward PrivateArea"); } } else { Log.Debug("Cant find Ward prefab"); } } SetUpGuard(__result, privateAreaRadius, patrolPoint); } } [HarmonyPatch(typeof(CharacterDrop), "OnDeath")] private static class CharacterDrop_OnDeath_Patch { [HarmonyPrefix] private static void CharacterDrop_OnDeath_Prefix(CharacterDrop __instance) { if (Cfg.allowLevels.Value) { Log.Debug("CharacterDrop_OnDeath_Patch_Prefix"); Character character = __instance.m_character; if ((Object)(object)character != (Object)null && !character.IsPlayer() && character.GetLevel() > 0) { s_tempCreatureLevel = character.GetLevel(); } } } [HarmonyPostfix] private static void CharacterDrop_OnDeath_Postfix(CharacterDrop __instance, ref CreatureInfo __state) { if (Cfg.allowLevels.Value) { Log.Debug("CharacterDrop_OnDeath_Patch_Postfix"); s_tempCreatureLevel = 0; } } } [HarmonyPatch(typeof(CharacterDrop), "DropItems")] [HarmonyPriority(0)] private static class CharacterDrop_DropItems_Patch { [HarmonyPrefix] private static bool CharacterDrop_DropItems_Prefix(CharacterDrop __instance, List> drops, Vector3 centerPos, float dropArea, bool ___m_dropsEnabled) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0070: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Invalid comparison between Unknown and I4 //IL_00fd: 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) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) Log.Debug("CharacterDrop_DropItems_Patch_Prefix"); if (!Cfg.allowLevels.Value || s_tempCreatureLevel <= 0) { return true; } foreach (KeyValuePair drop in drops) { for (int i = 0; i < drop.Value; i++) { Quaternion val = Quaternion.Euler(0f, (float)Random.Range(0, 360), 0f); Vector3 val2 = Random.insideUnitSphere * dropArea; GameObject obj = Object.Instantiate(drop.Key, centerPos + val2, val); ItemDrop component = obj.GetComponent(); if ((Object)(object)component != (Object)null && (int)component.m_itemData.m_shared.m_itemType == 13) { Log.Debug("Adding level to trophy"); component.m_itemData.m_quality = s_tempCreatureLevel; component.m_itemData.m_shared.m_maxQuality = s_tempCreatureLevel; component.m_itemData.m_worldLevel = (byte)Game.m_worldLevel; } Rigidbody component2 = obj.GetComponent(); if (Object.op_Implicit((Object)(object)component2)) { Vector3 insideUnitSphere = Random.insideUnitSphere; if (insideUnitSphere.y < 0f) { insideUnitSphere.y = 0f - insideUnitSphere.y; } component2.AddForce(insideUnitSphere * 5f, (ForceMode)2); } } } return false; } } [HarmonyPatch(typeof(Ragdoll), "SaveLootList")] private static class Ragdoll_SaveLootList_Patch { [HarmonyPostfix] private static void Ragdoll_SaveLootList_Postfix(Ragdoll __instance, CharacterDrop characterDrop, ZNetView ___m_nview) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) if (!Cfg.allowLevels.Value) { return; } Log.Debug("Ragdoll_SaveLootList_Patch_Postfix"); Character character = characterDrop.m_character; if ((Object)(object)character?.m_nview != (Object)null && !character.IsPlayer() && character.GetLevel() > 0) { ZDO zDO = __instance.m_nview.GetZDO(); if (zDO != null && zDO.IsValid()) { Log.Debug("Setting character level on Ragdoll zdo"); zDO.Set(TROPHY_LEVEL, character.GetLevel(), false); ZDOMan.instance.ForceSendZDO(zDO.m_uid); } } } } [HarmonyPatch(typeof(Ragdoll), "SpawnLoot")] private static class Ragdoll_SpawnLoot_Patch { [HarmonyPrefix] private static void Ragdoll_SpawnLoot_Prefix(Ragdoll __instance, Vector3 center) { if (Cfg.allowLevels.Value) { Log.Debug("Ragdoll_SpawnLoot_Patch_Postfix"); ZDO zDO = __instance.m_nview.GetZDO(); if (zDO.IsValid() && zDO.GetInt(TROPHY_LEVEL, 0) > 0) { s_tempCreatureLevel = zDO.GetInt(TROPHY_LEVEL, 0); } } } [HarmonyPostfix] private static void Ragdoll_SpawnLoot_Postfix(Ragdoll __instance, Vector3 center, ZNetView ___m_nview) { if (Cfg.allowLevels.Value) { Log.Debug("Ragdoll_SpawnLoot_Patch_Postfix"); s_tempCreatureLevel = 0; } } } [HarmonyPatch(typeof(BaseAI), "Flee")] private static class BaseAI_Flee_Patch { [HarmonyPrefix] private static bool BaseAI_Flee_Prefix(BaseAI __instance, float dt, Vector3 from) { ZDO val = __instance?.m_nview?.m_zdo; Vector3 val2 = default(Vector3); if (val != null && val.GetVec3(GUARD_PATROL_POINT, ref val2)) { return false; } return true; } } [HarmonyPatch(typeof(InventoryGrid), "UpdateGui")] private static class InventoryGrid_UpdateGui_Patch { [HarmonyPostfix] private static void InventoryGrid_UpdateGui_Postfix(InventoryGrid __instance) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Invalid comparison between Unknown and I4 if (!Cfg.allowLevels.Value) { return; } foreach (ItemData allItem in __instance.m_inventory.GetAllItems()) { if ((int)allItem.m_shared.m_itemType != 13) { continue; } Element element = __instance.GetElement(allItem.m_gridPos.x, allItem.m_gridPos.y, __instance.m_width); if (((Behaviour)element.m_icon).enabled) { int num = allItem.m_quality - 1; if (num > 0) { ((Behaviour)element.m_quality).enabled = true; element.m_quality.text = num.ToString(); } } } } } private static DefendYourBase _modInstance; private static string Mod = "DefendYourBase"; private static string LcMod = Mod.ToLower(); public static Logging Log; public static Faction tamedGuardFaction = (Faction)StringExtensionMethods.GetStableHashCode("TamedGuard"); private static string s_configPath; public static int GUARD_PATROL_POINT = StringExtensionMethods.GetStableHashCode("Tamed_Guard"); public static int WARD_FLAG = StringExtensionMethods.GetStableHashCode("Guarded_Ward"); public static string TROPHY_STRING = "DYB_CL_1847_"; public static int TROPHY_LEVEL = StringExtensionMethods.GetStableHashCode(TROPHY_STRING); private const string CANNOT_BE_SUMMONED = "$msg_neobotics_dyb_cant_summon"; internal static int s_tempCreatureLevel = 0; private static List prefabHashes = new List(); private static float privateAreaRadius = 0f; private static FileSystemWatcher mappingWatcher = null; private static List hauntNames = new List(); private static Harmony harmony = null; private static IDictionary trophyCreatureMap = new Dictionary(); private static Dictionary biomeValues = new Dictionary { { (Biome)1, 1 }, { (Biome)8, 2 }, { (Biome)2, 3 }, { (Biome)4, 4 }, { (Biome)16, 5 }, { (Biome)512, 6 }, { (Biome)32, 7 }, { (Biome)64, 8 }, { (Biome)256, 3 } }; private static Dictionary trophyBiomeMap = new Dictionary(); public static string creatureConfigString = "$item_trophy_greydwarf,1;$item_trophy_greydwarfbrute,1;$item_trophy_greydwarfshaman,1;$item_trophy_boar,1;$item_trophy_neck,1;$item_trophy_skeleton,1;$item_trophy_troll,2;$item_trophy_skeleton_hildir,3;$item_trophy_skeletonpoison,3;$item_trophy_wraith,3;$item_trophy_abomination,3;$item_trophy_blob,3;$item_trophy_draugr,3;$item_trophy_draugrelite,3;$item_trophy_leech,3;$item_trophy_surtling,3;$item_trophy_fenring,4;$item_trophy_cultist,4;$item_trophy_sgolem,4;$item_trophy_cultist_hildir,4;$item_trophy_ulv,4;$item_trophy_wolf,4;$item_trophy_goblin,5;$item_trophy_goblinbrute,5;$item_trophy_brutebro,5;$item_trophy_goblinshaman,5;$item_trophy_shamanbro,5;$item_trophy_deathsquito,5;$item_trophy_growth,5;$item_trophy_lox,5;$item_trophy_gjall,6;$item_trophy_dvergr,6;$item_trophy_seeker,6;$item_trophy_seeker_brute,6;$item_trophy_hatchling,4;$item_trophy_tick,6;$item_trophy_asksvin,7;$item_trophy_bonemaw,7;$item_trophy_charredarcher,7 ;$item_trophy_charredmage,7;$item_trophy_charredmelee,7;$item_trophy_fader,7;$item_trophy_fallenvalkyrie,7;$item_trophy_morgen,7;$item_trophy_volture,7;$item_trophy_serpent,3"; public static DefendYourBase GetInstance() { return _modInstance; } private void Awake() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown _modInstance = this; harmony = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID); harmony.PatchAll(Assembly.GetExecutingAssembly()); ConfigureMod(); Log.Info("Awake"); } private void ConfigureMod() { Log = Logging.GetLogger(Logging.LogLevels.Info, Mod); Cfg.BepInExConfig((BaseUnityPlugin)(object)_modInstance); s_configPath = ModResourceBootstrap.InitializeModResources(typeof(DefendYourBase), Mod); new Localizer(Mod); SetCreatureBiomeMapping(); BuildTrophyBiomeMap(creatureConfigString); if (Cfg.creatureBiomeMapping.Value != creatureConfigString) { Cfg.creatureBiomeMapping.Value = creatureConfigString; } } private void OnDestroy() { harmony.UnpatchSelf(); } private static void BuildTrophyBiomeMap(string stringMapping) { Log.Debug("BuildTrophyBiomeMap"); trophyBiomeMap.Clear(); string[] array = stringMapping.Split(';'); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(','); if (array2.Length == 2 && int.TryParse(array2[1], out var result)) { string text = array2[0].Trim(); if (!trophyBiomeMap.ContainsKey(text)) { trophyBiomeMap.Add(text, result); Log.Debug($"Added trophy {text} with biome {result}"); } } } } public static void ChangeBiomeMapping(object s, EventArgs e) { SettingChangedEventArgs e2 = (SettingChangedEventArgs)(object)((e is SettingChangedEventArgs) ? e : null); Log.Debug("ChangeBiomeMapping " + e2.ChangedSetting.Definition.Key); creatureConfigString = Cfg.creatureBiomeMapping.Value; BuildTrophyBiomeMap(Cfg.creatureBiomeMapping.Value); } public static bool SetCreatureBiomeMapping() { Log.Debug("SetCreatureBiomeMapping"); try { string text = Path.Combine(s_configPath, "CreatureBiomeMap.txt"); Log.Debug("Using config file " + text); if (!File.Exists(text)) { Log.Warning("Creature mapping file is missing. Using vanilla assignment."); return false; } string[] value = File.ReadAllLines(text); creatureConfigString = string.Join(";", value).Replace(" ", string.Empty); if (mappingWatcher == null) { mappingWatcher = Utils.CreateFileWatcher(text, ReloadMapping); Log.Debug("Created mapping file watcher"); } } catch (Exception) { Log.Warning("Can't read external creature mapping file. Using vanilla assignment."); return false; } return true; } private static void ReloadMapping() { Log.Debug("ReloadMapping"); if (SetCreatureBiomeMapping()) { Log.Debug("Reloading creature biome mapping from file"); if (Cfg.creatureBiomeMapping.Value != creatureConfigString) { Cfg.creatureBiomeMapping.Value = creatureConfigString; Log.Trace(Cfg.creatureBiomeMapping.Value); } } } private static void ReloadMapping(object sender, FileSystemEventArgs e) { ReloadMapping(); } public static void BuildTrophyCreatureMap() { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Invalid comparison between Unknown and I4 Log.Debug("BuildTrophyCreatureMap"); CharacterDrop val = default(CharacterDrop); ItemDrop val2 = default(ItemDrop); foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { Character component = (Character)(object)prefab.GetComponent(); if ((Object)(object)component == (Object)null) { component = prefab.GetComponent(); } if (!((Object)(object)component != (Object)null) || !prefab.TryGetComponent(ref val)) { continue; } foreach (Drop drop in val.m_drops) { if ((Object)(object)drop.m_prefab != (Object)null && drop.m_prefab.TryGetComponent(ref val2) && (int)val2.m_itemData.m_shared.m_itemType == 13 && !trophyCreatureMap.ContainsKey(val2.m_itemData.m_shared.m_name)) { trophyCreatureMap.Add(val2.m_itemData.m_shared.m_name, StringExtensionMethods.GetStableHashCode(((Object)prefab).name)); } } } } public static bool GetAllGuardedWardZDOs(List zdos) { Log.Debug("GetAllGuardedWardZDOs"); zdos.Clear(); foreach (ZDO value in ZDOMan.instance.m_objectsByID.Values) { if (value.IsValid() && value.GetBool(WARD_FLAG, false) && !zdos.Contains(value)) { zdos.Add(value); } } return zdos.Count > 0; } public static bool GetAllTamedZDOs(List zdos) { Log.Debug("GetAllTamedZDOs"); foreach (ZDO value in ZDOMan.instance.m_objectsByID.Values) { if (value.IsValid() && value.GetBool(ZDOVars.s_tamed, false)) { zdos.Add(value); } } return zdos.Count > 0; } public static bool TryGetGuardZDOs(List guards) { Log.Debug("TryGetGuardZDOs"); guards.Clear(); List list = new List(); Log.Debug("Searching for tamed Guard ZDOs"); GetAllTamedZDOs(list); Vector3 val = default(Vector3); foreach (ZDO item in list) { if (item.GetVec3(GUARD_PATROL_POINT, ref val)) { Log.Debug("Found bound Guard ZDO"); guards.Add(item); } } return guards.Count > 0; } private static bool CheckPiecesInRadius(Vector3 point, float dist, int piecesMin, int stationsMin, int bedsMin) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) Log.Debug("CheckPiecesInRadius"); int num = 0; int num2 = 0; int num3 = 0; List allInstances = WearNTear.GetAllInstances(); if (allInstances.Count < piecesMin + stationsMin) { return false; } CraftingStation val = default(CraftingStation); Bed val2 = default(Bed); foreach (WearNTear item in allInstances) { if (Vector3.Distance(point, ((Component)item).transform.position) < dist) { num++; if (((Component)item).gameObject.TryGetComponent(ref val)) { num2++; } if (((Component)item).gameObject.TryGetComponent(ref val2)) { num3++; } } if (num >= piecesMin && num2 >= stationsMin && num3 >= bedsMin) { return true; } } Log.Debug($"Base check failed: {num} pieces & {num2} stations {num3} beds found within radius {Cfg.proximityCheckRadius.Value}"); return false; } private static bool CheckGuardBiome(Vector3 location, string trophyName) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) Log.Debug("CheckGuardBiome"); if (biomeValues.TryGetValue(Heightmap.FindBiome(location), out var value) && value > 0 && trophyBiomeMap.TryGetValue(trophyName, out var value2) && value2 > 0) { Log.Debug($"Biome value heightmap {value} guard {value2} "); return value2 <= value; } return false; } private static int GetMaxBossLevel() { int num = 0; ZoneSystem instance = ZoneSystem.instance; if (instance.GetGlobalKey((GlobalKeys)33)) { num++; } if (instance.GetGlobalKey((GlobalKeys)36)) { num++; } if (instance.GetGlobalKey((GlobalKeys)37)) { num++; } if (instance.GetGlobalKey((GlobalKeys)34)) { num++; } if (instance.GetGlobalKey((GlobalKeys)35)) { num++; } if (instance.GetGlobalKey("defeated_queen")) { num++; } if (instance.GetGlobalKey("defeated_fader")) { num++; } return num; } private static bool CheckBossLevel(Vector3 location, string trophyName) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) Log.Debug("CheckBossLevel"); if (biomeValues.TryGetValue(Heightmap.FindBiome(location), out var value) && value > 0 && trophyBiomeMap.TryGetValue(trophyName, out var value2) && value2 > 0) { int maxBossLevel = GetMaxBossLevel(); Log.Debug($"Biome value heightmap {value} guard {value2} boss {maxBossLevel} "); return value2 <= maxBossLevel; } return false; } private static bool SetUpGuard(GameObject guard, float radius, Vector3 patrolPoint, int level = 0) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) Log.Debug("SetUpGuard"); if ((Object)(object)guard == (Object)null) { Log.Warning("Guard is missing in action. Can't setup"); return false; } Character component = guard.GetComponent(); if ((Object)(object)component == (Object)null) { Log.Warning("Guard is missing Character component. Can't setup"); return false; } MonsterAI component2 = guard.GetComponent(); if ((Object)(object)component2 == (Object)null) { Log.Warning("Guard is missing useable monster AI component. Can't setup"); return false; } component.m_tameableMonsterAI = component2; component.SetLevel(level); component.SetTamed(true); ((BaseAI)component2).m_patrol = true; ((BaseAI)component2).SetPatrolPoint(patrolPoint); ((BaseAI)component2).m_viewAngle = Mathf.Max(120f, ((BaseAI)component2).m_viewAngle); ((BaseAI)component2).m_viewRange = Mathf.Max(30f, ((BaseAI)component2).m_viewRange); component2.m_alertRange = Math.Max(30f, component2.m_alertRange); component2.m_maxChaseDistance = 0f; component2.m_circleTargetInterval = 0f; component2.m_circleTargetDuration = 2f; component2.m_circleTargetDistance = 4f; component.m_damageModifiers.m_fire = (DamageModifier)3; component.m_tolerateSmoke = true; if (Cfg.allowLevels.Value && level > 0) { component.m_level = level; } CharacterDrop val = default(CharacterDrop); if (guard.TryGetComponent(ref val)) { Object.Destroy((Object)(object)val); } return true; } public static bool TryGetGuardInScene(ZDO zdo, out GameObject guard) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) Log.Debug("TryGetGuardInScene"); guard = null; if (ZNetScene.instance.HaveInstance(zdo)) { Log.Debug("Guard has an instance"); guard = ZNetScene.instance.FindInstance(zdo.m_uid); if ((Object)(object)guard != (Object)null) { Log.Debug("Found Guard instance in scene"); return true; } } return false; } private static void SendOneDestroyed(ZDOID zdoid) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_000e: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(1); val.Write(zdoid); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "DestroyZDO", new object[1] { val }); } private static void ConsumeItem(Humanoid user, ItemData item) { user.GetInventory().RemoveOneItem(item); } private static void SendAllDestroyed(List zdos) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0025: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(zdos.Count); foreach (ZDO zdo in zdos) { val.Write(zdo.m_uid); } ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "DestroyZDO", new object[1] { val }); } } public class Localizer { [HarmonyPatch(typeof(Localization), "SetupLanguage")] private static class Localization_SetupLanguage_Patch { [HarmonyPostfix] private static void Postfix(Localization __instance) { //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown try { if (string.IsNullOrEmpty(LocFolder)) { return; } string selectedLanguage = __instance.GetSelectedLanguage(); string path = Path.Combine(LocFolder, selectedLanguage + ".loc"); if (!File.Exists(path)) { Logging.Instance.Warning("[" + ModName + "] Localization file not found for " + selectedLanguage + ", trying English fallback"); path = Path.Combine(LocFolder, "English.loc"); if (!File.Exists(path)) { Logging.Instance.Warning("[" + ModName + "] English fallback localization file not found"); return; } } TextAsset val = new TextAsset(File.ReadAllText(path)); __instance.LoadCSV(val, selectedLanguage); Logging.Instance.Info("[" + ModName + "] Loaded localization file: " + Path.GetFileName(path)); } catch (Exception ex) { Logging.Instance.Error("Localization load failed: " + ex); } } } internal static string ModName; internal static string LocFolder; public Localizer(string modName) { ModName = modName; LocFolder = ConfigFolderHelper.GetOrCreateModConfigFolder(modName); } public string Localize(string key) { return Localization.instance.Localize(key); } } }