Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Vanilla Fix v1.2.0
plugins/VanillaFix.dll
Decompiled 43 minutes ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: AssemblyFileVersion("1.2.0.0")] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyTitle("Vanilla Fix")] [assembly: AssemblyProduct("Vanilla Fix")] [assembly: AssemblyVersion("1.2.0.0")] namespace VanillaFix; internal abstract class Module { private ConfigEntry<bool> m_enabled; private bool m_wasEnabled; private bool m_broken; private float m_quietUntil; public abstract string Title { get; } protected abstract string Section { get; } protected abstract bool DefaultOn { get; } protected abstract string Description { get; } public bool Enabled { get { if (m_enabled != null && m_enabled.Value) { return !m_broken; } return false; } } public string SectionName => Section; public void Bind(ConfigFile cfg) { m_enabled = cfg.Bind<bool>(Section, "Enabled", DefaultOn, Description); BindSettings(cfg); } protected abstract void BindSettings(ConfigFile cfg); protected abstract void Run(); protected virtual void FixedRun() { } protected virtual void OnDisable() { } public void Tick() { if (!Enabled) { if (m_wasEnabled) { m_wasEnabled = false; SafeDisable(); } return; } m_wasEnabled = true; try { Run(); } catch (Exception e) { Report(e); } } public void FixedTick() { if (!m_wasEnabled || !Enabled) { return; } try { FixedRun(); } catch (Exception e) { Report(e); } } private void Report(Exception e) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup >= m_quietUntil) { Plugin.Log.LogWarning((object)(Title + ": " + e)); m_quietUntil = realtimeSinceStartup + 60f; } } public void Shutdown() { if (m_wasEnabled) { SafeDisable(); } } private void SafeDisable() { try { OnDisable(); } catch (Exception ex) { Plugin.Log.LogWarning((object)(Title + " (switching off): " + ex.Message)); } } protected void Broken(string what) { m_broken = true; Plugin.Log.LogError((object)(Title + " switched off: game members not found (" + what + "). The game has probably changed.")); } } internal sealed class CampFires : Module { private struct Entry { public object Zdo; public int Hash; public Vector3 Pos; public bool Owned; } private sealed class Camp { public string Name; public Vector3 Pos; public float Radius; public readonly List<Entry> Fires = new List<Entry>(); public readonly List<Entry> Spawners = new List<Entry>(); public float ListedAt = -1000f; public int LastState = -1; public int LastWaiting; public bool Seen; } private const float BaseReach = 45f; private const long StaleTicks = 100000000L; private const float BaseSettle = 2f; private ConfigEntry<string> m_locations; private ConfigEntry<float> m_campRadius; private ConfigEntry<float> m_holdRadius; private ConfigEntry<float> m_interval; private ConfigEntry<float> m_refillBelow; private ConfigEntry<bool> m_log; private bool m_resolved; private Type tFireplace; private Type tSpawner; private Type tEffectArea; private FieldInfo fAllLocations; private FieldInfo fInstances; private FieldInfo fNamedPrefabs; private FieldInfo fBySector; private FieldInfo fMaxFuel; private FieldInfo fInfinite; private FieldInfo fSecPerFuel; private FieldInfo fSector; private FieldInfo fBaseObject; private FieldInfo fAreaType; private MethodInfo mGetConnection; private MethodInfo mIsNone; private MethodInfo mGetZdo; private MethodInfo mIsOwner; private MethodInfo mUpdateState; private MethodInfo mSectorToIndex; private MethodInfo mGetTime; private MethodInfo mInsideArea; private FieldInfo fConnType; private FieldInfo fConnTarget; private int kFuel; private int kCreator; private int kLastTime; private object[] m_insideArgs; private object m_scene; private readonly HashSet<int> m_fireHashes = new HashSet<int>(); private readonly HashSet<int> m_spawnerHashes = new HashSet<int>(); private readonly HashSet<int> m_baseHashes = new HashSet<int>(); private readonly HashSet<int> m_naturalOffHashes = new HashSet<int>(); private readonly Dictionary<long, Camp> m_camps = new Dictionary<long, Camp>(); private readonly List<IList> m_sectors = new List<IList>(); private readonly List<Vector3> m_holders = new List<Vector3>(); private readonly List<Vector3> m_loadingBase = new List<Vector3>(); private readonly Dictionary<Object, float> m_baseSeen = new Dictionary<Object, float>(); private Dictionary<string, float> m_wanted; private string m_wantedKey; private float m_nextTick; public override string Title => "Camp Fires"; protected override string Section => "02 - Camp Fires"; protected override bool DefaultOn => true; protected override string Description => "Keep the fires of Fuling camps burning while the camp around them is still held. A fire is kept while a camp spawner that has not spawned yet, or a Fuling the camp spawned, is within the hold radius of it and the fire is not inside a player base."; protected override void BindSettings(ConfigFile cfg) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Expected O, but got Unknown m_locations = cfg.Bind<string>(Section, "Locations", "GoblinCamp2, GoblinCamp2_1, Hildir_plainsfortress:20", "Location prefab names whose natural fires are kept (comma separated). 'Name:20' gives that location its own radius."); m_campRadius = cfg.Bind<float>(Section, "Camp radius", 35f, new ConfigDescription("Fires and spawners within this horizontal distance (m) of the location centre belong to the camp.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), new object[0])); m_holdRadius = cfg.Bind<float>(Section, "Hold radius", 20f, new ConfigDescription("A fire is kept only while a camp spawner that has not spawned yet, or a creature spawned by one of the camp's spawners, is within this horizontal distance (m) of it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 60f), new object[0])); m_refillBelow = cfg.Bind<float>(Section, "Refill below", 0.9f, new ConfigDescription("A kept fire is filled up when its fuel drops below this fraction of the maximum.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[0])); m_interval = cfg.Bind<float>(Section, "Check interval", 2f, new ConfigDescription("Seconds between checks.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 30f), new object[0])); m_log = cfg.Bind<bool>(Section, "Log", false, "Log when the number of fires kept in a camp changes, and when fires are refilled or have to wait."); } private bool Resolve() { //IL_051a: Unknown result type (might be due to invalid IL or missing references) if (m_resolved) { return true; } Type type = R.Type("ZNetScene"); if (type == null) { return false; } Type t = R.Type("ZDOMan"); Type t2 = R.Type("ZoneSystem"); Type t3 = R.Type("ZDO"); Type type2 = R.Type("ZDOID"); Type t4 = R.Type("Location"); Type t5 = R.Type("ZNetView"); Type t6 = R.Type("ZNet"); tFireplace = R.Type("Fireplace"); tSpawner = R.Type("CreatureSpawner"); tEffectArea = R.Type("EffectArea"); Type type3 = R.Type("EffectArea+Type"); fAllLocations = R.Field(t4, "s_allLocations"); fInstances = R.Field(type, "m_instances"); fNamedPrefabs = R.Field(type, "m_namedPrefabs"); fBySector = R.Field(t, "m_objectsBySector"); fMaxFuel = R.Field(tFireplace, "m_maxFuel"); fInfinite = R.Field(tFireplace, "m_infiniteFuel"); fSecPerFuel = R.Field(tFireplace, "m_secPerFuel"); fBaseObject = R.Field(tFireplace, "m_playerBaseObject"); fAreaType = R.Field(tEffectArea, "m_type"); fSector = R.Field(R.Type("ZoneSystem+SectorIndex"), "Sector"); mIsNone = R.Method(type2, "IsNone"); mGetZdo = ((type2 != null) ? R.Method(t, "GetZDO", type2) : null); mIsOwner = R.Method(t5, "IsOwner"); mUpdateState = R.Method(tFireplace, "UpdateState"); mSectorToIndex = R.Method(t2, "SectorToIndex", typeof(int), typeof(int)); mGetTime = R.Method(t6, "GetTime"); mGetConnection = R.Method(t3, "GetConnection"); mInsideArea = ((type3 != null) ? R.Method(tEffectArea, "IsPointInsideArea", typeof(Vector3), type3, typeof(float)) : null); Type t7 = R.Type("ZDOConnection"); fConnType = R.Field(t7, "m_type"); fConnTarget = R.Field(t7, "m_target"); kFuel = Zdo.Var("s_fuel"); kCreator = Zdo.Var("s_creator"); kLastTime = Zdo.Var("s_lastTime"); string text = R.Missing(fAllLocations, "Location.s_allLocations", fInstances, "ZNetScene.m_instances", fNamedPrefabs, "ZNetScene.m_namedPrefabs", fBySector, "ZDOMan.m_objectsBySector", fMaxFuel, "Fireplace.m_maxFuel", fInfinite, "Fireplace.m_infiniteFuel", fSecPerFuel, "Fireplace.m_secPerFuel", fSector, "SectorIndex.Sector", mIsNone, "ZDOID.IsNone", mGetZdo, "ZDOMan.GetZDO", mIsOwner, "ZNetView.IsOwner", mUpdateState, "Fireplace.UpdateState", mSectorToIndex, "ZoneSystem.SectorToIndex", mGetTime, "ZNet.GetTime", mGetConnection, "ZDO.GetConnection", fConnType, "ZDOConnection.m_type", fConnTarget, "ZDOConnection.m_target", fBaseObject, "Fireplace.m_playerBaseObject", fAreaType, "EffectArea.m_type", mInsideArea, "EffectArea.IsPointInsideArea", tSpawner, "CreatureSpawner"); if (text == null && (kFuel == 0 || kCreator == 0 || kLastTime == 0)) { text = "ZDOVars"; } if (text == null && !Zdo.Ready()) { text = "ZDO accessors"; } if (text != null) { Broken(text); return false; } m_insideArgs = new object[3] { Vector3.zero, Enum.ToObject(type3, 4), 0f }; m_resolved = true; return true; } protected override void Run() { float realtimeSinceStartup = Time.realtimeSinceStartup; bool flag = OwnershipGained(); if (realtimeSinceStartup < m_nextTick && !flag) { return; } m_nextTick = realtimeSinceStartup + m_interval.Value; if (!Resolve()) { return; } object obj = R.Instance(R.Type("ZNetScene")); if ((Object)((obj is Object) ? obj : null) == (Object)null) { m_scene = null; m_camps.Clear(); return; } object obj2 = R.Instance(R.Type("ZDOMan")); if (obj2 == null || (Object)(object)R.LocalPlayer() == (Object)null) { return; } if (!object.ReferenceEquals(obj, m_scene)) { NewWorld(obj); } ParseLocations(); UpdateCamps(); if (m_camps.Count == 0 || !(fInstances.GetValue(obj) is IDictionary instances)) { return; } foreach (Camp value in m_camps.Values) { if (value.Seen) { CheckCamp(value, obj2, instances); } } } private bool OwnershipGained() { if (Zdo.IsOwner == null || m_camps.Count == 0) { return false; } bool result = false; foreach (Camp value2 in m_camps.Values) { if (!value2.Seen) { continue; } for (int i = 0; i < value2.Fires.Count; i++) { Entry value = value2.Fires[i]; bool flag = Zdo.IsOwner(value.Zdo); if (flag != value.Owned) { if (flag) { result = true; } value.Owned = flag; value2.Fires[i] = value; } } } return result; } private long WorldTicks() { if (!(mGetTime.Invoke(R.Instance(R.Type("ZNet")), null) is DateTime dateTime)) { return 0L; } return dateTime.Ticks; } private void NewWorld(object scene) { m_scene = scene; m_fireHashes.Clear(); m_spawnerHashes.Clear(); m_baseHashes.Clear(); m_naturalOffHashes.Clear(); m_camps.Clear(); m_baseSeen.Clear(); if (!(fNamedPrefabs.GetValue(scene) is IDictionary dictionary)) { return; } foreach (DictionaryEntry item2 in dictionary) { object? value = item2.Value; GameObject val = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val == (Object)null) { continue; } int item = Convert.ToInt32(item2.Key); Component component = val.GetComponent(tFireplace); if ((Object)(object)component != (Object)null && !(bool)fInfinite.GetValue(component) && Convert.ToSingle(fSecPerFuel.GetValue(component)) > 0f) { m_fireHashes.Add(item); } if ((Object)(object)val.GetComponent(tSpawner) != (Object)null) { m_spawnerHashes.Add(item); } if (HasBaseArea(val)) { m_baseHashes.Add(item); if ((Object)(object)component != (Object)null && (Object)/*isinst with value type is only supported in some contexts*/ != (Object)null) { m_naturalOffHashes.Add(item); } } } } private bool HasBaseArea(GameObject go) { Component[] componentsInChildren = go.GetComponentsInChildren(tEffectArea, true); for (int i = 0; i < componentsInChildren.Length; i++) { if ((Convert.ToInt32(fAreaType.GetValue(componentsInChildren[i])) & 4) != 0) { return true; } } return false; } private void ParseLocations() { string text = m_locations.Value ?? ""; if (text == m_wantedKey && m_wanted != null) { return; } m_wantedKey = text; m_wanted = new Dictionary<string, float>(StringComparer.Ordinal); string[] array = text.Split(','); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); float value = 0f; int num = text2.IndexOf(':'); if (num >= 0) { if (float.TryParse(text2.Substring(num + 1).Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && result > 0f) { value = result; } text2 = text2.Substring(0, num).Trim(); } if (text2.Length > 0) { m_wanted[text2] = value; } } m_camps.Clear(); } private void UpdateCamps() { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) IList list = fAllLocations.GetValue(null) as IList; foreach (Camp value3 in m_camps.Values) { value3.Seen = false; } if (list != null) { for (int i = 0; i < list.Count; i++) { object? obj = list[i]; Component val = (Component)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null) { continue; } string text = R.PrefabName(val.gameObject); if (m_wanted.TryGetValue(text, out var value)) { Vector3 position = val.transform.position; long key = ((long)Mathf.RoundToInt(position.x * 2f) << 32) ^ (uint)Mathf.RoundToInt(position.z * 2f); if (!m_camps.TryGetValue(key, out var value2) || value2.Name != text) { value2 = new Camp(); value2.Name = text; value2.Pos = position; m_camps[key] = value2; } value2.Radius = ((value > 0f) ? value : m_campRadius.Value); value2.Seen = true; } } } if (m_camps.Count <= 256) { return; } List<long> list2 = new List<long>(); foreach (KeyValuePair<long, Camp> camp in m_camps) { if (!camp.Value.Seen) { list2.Add(camp.Key); } } for (int j = 0; j < list2.Count; j++) { m_camps.Remove(list2[j]); } } private static int ZoneCoord(float v) { return Mathf.FloorToInt((v + 32f) / 64f); } private void Sectors(object zdoman, Vector3 centre, float radius) { m_sectors.Clear(); if (!(fBySector.GetValue(zdoman) is Array array)) { return; } int num = ZoneCoord(centre.x - radius); int num2 = ZoneCoord(centre.x + radius); int num3 = ZoneCoord(centre.z - radius); int num4 = ZoneCoord(centre.z + radius); for (int i = num; i <= num2; i++) { for (int j = num3; j <= num4; j++) { object obj = mSectorToIndex.Invoke(null, new object[2] { i, j }); int num5 = (int)Convert.ToUInt32(fSector.GetValue(obj)); if (num5 > 0 && num5 < array.Length && array.GetValue(num5) is IList item) { m_sectors.Add(item); } } } } private void ListCamp(Camp c, object zdoman) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) c.Fires.Clear(); c.Spawners.Clear(); c.ListedAt = Time.time; Sectors(zdoman, c.Pos, c.Radius); Entry item = default(Entry); for (int i = 0; i < m_sectors.Count; i++) { IList list = m_sectors[i]; for (int j = 0; j < list.Count; j++) { object obj = list[j]; if (obj == null) { continue; } int num = Zdo.Prefab(obj); bool flag = m_fireHashes.Contains(num); if (!flag && !m_spawnerHashes.Contains(num)) { continue; } Vector3 val = Zdo.Position(obj); if (!(R.Flat(val, c.Pos) > c.Radius) && (!flag || Zdo.GetLong(obj, kCreator, 0L) == 0)) { item.Zdo = obj; item.Hash = num; item.Pos = val; item.Owned = flag && Zdo.IsOwner != null && Zdo.IsOwner(obj); if (flag) { c.Fires.Add(item); } else { c.Spawners.Add(item); } } } } } private void ListLoadingBase(Camp c, object zdoman, IDictionary instances) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) m_loadingBase.Clear(); float num = c.Radius + 45f; float realtimeSinceStartup = Time.realtimeSinceStartup; Sectors(zdoman, c.Pos, num); for (int i = 0; i < m_sectors.Count; i++) { IList list = m_sectors[i]; for (int j = 0; j < list.Count; j++) { object obj = list[j]; if (obj == null) { continue; } int item = Zdo.Prefab(obj); if (!m_baseHashes.Contains(item) || (m_naturalOffHashes.Contains(item) && Zdo.GetLong(obj, kCreator, 0L) == 0)) { continue; } Vector3 val = Zdo.Position(obj); if (!(R.Flat(val, c.Pos) > num)) { Object val2 = (Object)(instances.Contains(obj) ? /*isinst with value type is only supported in some contexts*/: null); float value; if (val2 == (Object)null) { m_loadingBase.Add(val); } else if (!m_baseSeen.TryGetValue(val2, out value)) { m_baseSeen[val2] = realtimeSinceStartup; m_loadingBase.Add(val); } else if (realtimeSinceStartup - value < 2f) { m_loadingBase.Add(val); } } } } if (m_baseSeen.Count > 256) { PruneBaseSeen(); } } private void PruneBaseSeen() { List<Object> list = new List<Object>(); foreach (KeyValuePair<Object, float> item in m_baseSeen) { if (item.Key == (Object)null) { list.Add(item.Key); } } for (int i = 0; i < list.Count; i++) { m_baseSeen.Remove(list[i]); } } private static bool StillValid(Entry e) { //IL_0026: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (Zdo.Prefab(e.Zdo) == e.Hash) { Vector3 val = Zdo.Position(e.Zdo) - e.Pos; return ((Vector3)(ref val)).sqrMagnitude < 0.01f; } return false; } private bool InPlayerBase(Vector3 p) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) m_insideArgs[0] = p; object? obj = mInsideArea.Invoke(null, m_insideArgs); return (Object)((obj is Object) ? obj : null) != (Object)null; } private void CheckCamp(Camp c, object zdoman, IDictionary instances) { //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_0372: Unknown result type (might be due to invalid IL or missing references) //IL_037c: Unknown result type (might be due to invalid IL or missing references) bool flag = Time.time - c.ListedAt <= 30f; int num = 0; while (flag && num < c.Fires.Count) { flag = StillValid(c.Fires[num]); num++; } int num2 = 0; while (flag && num2 < c.Spawners.Count) { flag = StillValid(c.Spawners[num2]); num2++; } if (!flag) { ListCamp(c, zdoman); } if (c.Fires.Count == 0) { return; } bool flag2 = false; for (int i = 0; i < c.Fires.Count; i++) { if (flag2) { break; } flag2 = instances.Contains(c.Fires[i].Zdo); } if (!flag2) { return; } m_holders.Clear(); int num3 = 0; int num4 = 0; for (int j = 0; j < c.Spawners.Count; j++) { object obj = mGetConnection.Invoke(c.Spawners[j].Zdo, null); if (obj == null || Convert.ToInt32(fConnType.GetValue(obj)) != 3) { num3++; m_holders.Add(c.Spawners[j].Pos); continue; } object value = fConnTarget.GetValue(obj); if (!(bool)mIsNone.Invoke(value, null)) { object obj2 = mGetZdo.Invoke(zdoman, new object[1] { value }); if (obj2 != null) { num4++; m_holders.Add(Zdo.Position(obj2)); } } } float value2 = m_holdRadius.Value; float num5 = Mathf.Clamp01(m_refillBelow.Value); int num6 = 0; int num7 = 0; int num8 = 0; int num9 = 0; int num10 = 0; int num11 = 0; bool flag3 = false; long num12 = -1L; for (int k = 0; k < c.Fires.Count; k++) { Vector3 pos = c.Fires[k].Pos; bool flag4 = false; for (int l = 0; l < m_holders.Count; l++) { if (flag4) { break; } flag4 = R.Flat(pos, m_holders[l]) <= value2; } if (!flag4) { continue; } object zdo = c.Fires[k].Zdo; Component val = (Component)(instances.Contains(zdo) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val == (Object)null) { num6++; continue; } if (InPlayerBase(pos)) { num7++; continue; } num6++; if (!(bool)mIsOwner.Invoke(val, null)) { continue; } Component component = val.GetComponent(tFireplace); if ((Object)(object)component == (Object)null) { continue; } float num13 = Convert.ToSingle(fMaxFuel.GetValue(component)); float num14 = Zdo.GetFloat(zdo, kFuel, 0f); bool flag5 = num14 < num13 * num5; if (num12 < 0) { num12 = WorldTicks(); } if (!flag5) { long num15 = Zdo.GetLong(zdo, kLastTime, 0L); if (num15 <= 0 || num12 - num15 <= 100000000) { continue; } } if (!flag3) { ListLoadingBase(c, zdoman, instances); flag3 = true; } bool flag6 = false; for (int m = 0; m < m_loadingBase.Count; m++) { if (flag6) { break; } flag6 = R.Flat(pos, m_loadingBase[m]) <= 45f; } if (flag6) { num8++; continue; } if (num12 > 0) { Zdo.SetLong(zdo, kLastTime, num12); } if (!flag5) { num11++; continue; } Zdo.SetFloat(zdo, kFuel, num13); mUpdateState.Invoke(component, null); num9++; if (num14 <= 0f) { num10++; } } int num16 = num6 | (num7 << 10) | (c.Fires.Count << 20); if (m_log.Value) { string text = "Camp Fires: " + c.Name + " (" + c.Pos.x.ToString("0", CultureInfo.InvariantCulture) + ", " + c.Pos.z.ToString("0", CultureInfo.InvariantCulture) + ")"; if (num16 != c.LastState) { Plugin.Log.LogInfo((object)(text + ": keeping " + num6 + " of " + c.Fires.Count + " fires (" + num7 + " inside a player base, " + (c.Fires.Count - num6 - num7) + " with nothing within " + value2.ToString("0.#", CultureInfo.InvariantCulture) + " m; " + c.Spawners.Count + " spawners: " + num3 + " not spawned yet, " + num4 + " with their creature alive)")); } if (num8 > 0 && num8 != c.LastWaiting) { Plugin.Log.LogInfo((object)(text + ": " + num8 + " fire(s) left alone for now, a player-base piece near them is still loading")); } if (num11 > 0) { Plugin.Log.LogInfo((object)(text + ": " + num11 + " full fire(s) not charged for the time they spent out of range")); } if (num9 > 0) { Plugin.Log.LogInfo((object)(text + ": refilled " + num9 + " fire(s)" + ((num10 > 0) ? (", " + num10 + " of them were out") : ""))); } } c.LastState = num16; c.LastWaiting = num8; } } internal sealed class FireWarmth : Module { private ConfigEntry<float> m_interval; private ConfigEntry<float> m_margin; private ConfigEntry<bool> m_log; private bool m_resolved; private FieldInfo fAll; private FieldInfo fList; private FieldInfo fHeat; private FieldInfo fSeHash; private FieldInfo fCollider; private FieldInfo fCharCollider; private float m_next; public override string Title => "Fire Warmth"; protected override string Section => "08 - Fire Warmth"; protected override bool DefaultOn => true; protected override string Description => "If rain puts out a fire's warmth while you stand by it and you then walk away, the game keeps treating you as standing by that fire once it dries, wherever you are. Clear that leftover as soon as you are clearly away."; protected override void BindSettings(ConfigFile cfg) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown m_interval = cfg.Bind<float>(Section, "Check interval", 1f, new ConfigDescription("Seconds between checks.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 10f), new object[0])); m_margin = cfg.Bind<float>(Section, "Margin", 1f, new ConfigDescription("How far (m) outside an area's bounding box you must be before its leftover is cleared. Only areas you are certainly out of are touched.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), new object[0])); m_log = cfg.Bind<bool>(Section, "Log", true, "Log each leftover that is cleared (it only happens when the game missed your exit)."); } private bool Resolve() { if (m_resolved) { return true; } Type type = R.Type("EffectArea"); Type type2 = R.Type("Character"); if (type == null || type2 == null) { return false; } fAll = R.Field(type, "s_allAreas"); fList = R.Field(type, "m_collidedWithCharacter"); fHeat = R.Field(type, "m_isHeatType"); fSeHash = R.Field(type, "m_statusEffectHash"); fCollider = R.Field(type, "m_collider"); fCharCollider = R.Field(type2, "m_collider"); string text = R.Missing(fAll, "EffectArea.s_allAreas", fList, "EffectArea.m_collidedWithCharacter", fHeat, "EffectArea.m_isHeatType", fSeHash, "EffectArea.m_statusEffectHash", fCollider, "EffectArea.m_collider", fCharCollider, "Character.m_collider"); if (text != null) { Broken(text); return false; } m_resolved = true; return true; } protected override void Run() { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < m_next) { return; } m_next = realtimeSinceStartup + m_interval.Value; if (!Resolve()) { return; } Component val = R.LocalPlayer(); if ((Object)(object)val == (Object)null || !(fAll.GetValue(null) is IList { Count: not 0 } list)) { return; } object? value = fCharCollider.GetValue(val); Collider val2 = (Collider)((value is Collider) ? value : null); Bounds val3 = (Bounds)(((Object)(object)val2 != (Object)null && val2.enabled) ? val2.bounds : new Bounds(val.transform.position + Vector3.up, new Vector3(1f, 2f, 1f))); float num = 2f * m_margin.Value; for (int i = 0; i < list.Count; i++) { object? obj = list[i]; Behaviour val4 = (Behaviour)((obj is Behaviour) ? obj : null); if ((Object)(object)val4 == (Object)null || !val4.isActiveAndEnabled || !(fList.GetValue(val4) is IList { Count: not 0 } list2) || !list2.Contains(val) || (!(bool)fHeat.GetValue(val4) && (int)fSeHash.GetValue(val4) == 0)) { continue; } object? value2 = fCollider.GetValue(val4); Collider val5 = (Collider)((value2 is Collider) ? value2 : null); if ((Object)(object)val5 == (Object)null || !val5.enabled) { continue; } Bounds bounds = val5.bounds; ((Bounds)(ref bounds)).Expand(num); if (!((Bounds)(ref bounds)).Intersects(val3)) { list2.Remove(val); if (m_log.Value) { Bounds bounds2 = val5.bounds; Vector3 center = ((Bounds)(ref bounds2)).center; Plugin.Log.LogInfo((object)("Fire Warmth: " + R.PrefabName(((Component)((Component)val4).transform.root).gameObject) + " (" + center.x.ToString("0.0", CultureInfo.InvariantCulture) + ", " + center.z.ToString("0.0", CultureInfo.InvariantCulture) + ") still counted you as standing in it " + Vector3.Distance(center, val.transform.position).ToString("0", CultureInfo.InvariantCulture) + " m away; cleared.")); } } } } } internal sealed class GroundItemMerging : Module { private const int VanillaThreshold = 200; private ConfigEntry<int> m_threshold; private ConfigEntry<bool> m_ignoreFish; private ConfigEntry<bool> m_log; private bool m_resolved; private FieldInfo fInstances; private FieldInfo fHaveStacked; private FieldInfo fNview; private MethodInfo mAutoStack; private MethodInfo mIsValid; private MethodInfo mIsOwner; private Type tFish; private readonly Dictionary<int, bool> m_isFish = new Dictionary<int, bool>(); private readonly HashSet<int> m_handled = new HashSet<int>(); private readonly List<Component> m_held = new List<Component>(); private readonly HashSet<int> m_called = new HashSet<int>(); private int m_state = -1; private int m_lastLogged = -1; private float m_nextTick; private float m_nextLog; public override string Title => "Ground Item Merging"; protected override string Section => "03 - Ground Item Merging"; protected override bool DefaultOn => true; protected override string Description => "Ground items start merging into stacks when more than 200 are loaded, and every fish swimming nearby counts as one. Leave fish out of the count, and optionally change the threshold."; protected override void BindSettings(ConfigFile cfg) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown m_threshold = cfg.Bind<int>(Section, "Threshold", 200, new ConfigDescription("Ground items merge only while more than this many are loaded. Vanilla is 200. Higher = later (100000 = practically never), lower = sooner.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000000), new object[0])); m_ignoreFish = cfg.Bind<bool>(Section, "Ignore fish", true, "Leave fish (the ones swimming in the loaded sea) out of the count. While the count without fish is at or below the threshold, nothing merges, fish included."); m_log = cfg.Bind<bool>(Section, "Log", false, "Log when merging starts or stops being allowed."); } private bool Resolve() { if (m_resolved) { return true; } Type type = R.Type("ItemDrop"); if (type == null) { return false; } Type t = R.Type("ZNetView"); tFish = R.Type("Fish"); fInstances = R.Field(type, "s_instances"); fHaveStacked = R.Field(type, "m_haveAutoStacked"); fNview = R.Field(type, "m_nview"); mAutoStack = R.Method(type, "AutoStackItems"); mIsValid = R.Method(t, "IsValid"); mIsOwner = R.Method(t, "IsOwner"); string text = R.Missing(fInstances, "ItemDrop.s_instances", fHaveStacked, "ItemDrop.m_haveAutoStacked", fNview, "ItemDrop.m_nview", mAutoStack, "ItemDrop.AutoStackItems", mIsValid, "ZNetView.IsValid", mIsOwner, "ZNetView.IsOwner"); if (text == null && fHaveStacked.FieldType != typeof(bool)) { text = "ItemDrop.m_haveAutoStacked (type)"; } if (text != null) { Broken(text); return false; } m_resolved = true; return true; } private void ReleaseHeld() { for (int i = 0; i < m_held.Count; i++) { if ((Object)(object)m_held[i] != (Object)null) { fHaveStacked.SetValue(m_held[i], false); } } m_held.Clear(); m_handled.Clear(); } protected override void OnDisable() { if (m_resolved) { ReleaseHeld(); } m_state = -1; } protected override void Run() { float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < m_nextTick) { return; } m_nextTick = realtimeSinceStartup + 0.5f; if (!Resolve() || !(fInstances.GetValue(null) is IList list)) { return; } int value = m_threshold.Value; bool flag = m_ignoreFish.Value && tFish != null; if (value == 200 && !flag) { if (m_held.Count > 0) { ReleaseHeld(); } m_state = -1; return; } int count = list.Count; int num = count; if (flag) { for (int i = 0; i < list.Count; i++) { object? obj = list[i]; Component val = (Component)((obj is Component) ? obj : null); if (!((Object)(object)val == (Object)null)) { int instanceID = ((Object)val).GetInstanceID(); if (!m_isFish.TryGetValue(instanceID, out var value2)) { value2 = (Object)(object)val.GetComponent(tFish) != (Object)null; m_isFish[instanceID] = value2; } if (value2) { num--; } } } if (m_isFish.Count > count * 4 + 256) { PruneFish(list); } } int num2 = ((num > value) ? 1 : 0); if (num2 != m_state) { if (num2 == 1) { ReleaseHeld(); } m_state = num2; } if (m_log.Value && num2 != m_lastLogged && realtimeSinceStartup >= m_nextLog) { Plugin.Log.LogInfo((object)("Ground Item Merging: " + count + " ground items loaded" + (flag ? (" (" + num + " without fish)") : "") + ", threshold " + value + ((num2 == 1) ? ": merging allowed" : ": merging held back"))); m_lastLogged = num2; m_nextLog = realtimeSinceStartup + 10f; } if (num2 == 0) { for (int j = 0; j < list.Count; j++) { object? obj2 = list[j]; Component val2 = (Component)((obj2 is Component) ? obj2 : null); if (!((Object)(object)val2 == (Object)null) && m_handled.Add(((Object)val2).GetInstanceID()) && !(bool)fHaveStacked.GetValue(val2)) { fHaveStacked.SetValue(val2, true); m_held.Add(val2); } } if (m_handled.Count > list.Count * 4 + 256) { Prune(list, m_handled); } if (m_held.Count > list.Count * 2 + 64) { m_held.RemoveAll((Component c) => (Object)(object)c == (Object)null); } } else { if (count > 200) { return; } object[] array = new object[list.Count]; list.CopyTo(array, 0); int num3 = 0; for (int num4 = 0; num4 < array.Length; num4++) { if (num3 >= 50) { break; } object obj3 = array[num4]; Component val3 = (Component)((obj3 is Component) ? obj3 : null); if ((Object)(object)val3 == (Object)null || !m_called.Add(((Object)val3).GetInstanceID())) { continue; } object value3 = fNview.GetValue(val3); if (!((Object)((value3 is Object) ? value3 : null) == (Object)null)) { if (!(bool)mIsValid.Invoke(value3, null) || !(bool)mIsOwner.Invoke(value3, null)) { m_called.Remove(((Object)val3).GetInstanceID()); continue; } mAutoStack.Invoke(val3, null); num3++; } } if (m_called.Count > list.Count * 4 + 256) { Prune(list, m_called); } } } private void PruneFish(IList list) { HashSet<int> hashSet = Alive(list); List<int> list2 = new List<int>(); foreach (int key in m_isFish.Keys) { if (!hashSet.Contains(key)) { list2.Add(key); } } for (int i = 0; i < list2.Count; i++) { m_isFish.Remove(list2[i]); } } private static void Prune(IList list, HashSet<int> set) { set.IntersectWith(Alive(list)); } private static HashSet<int> Alive(IList list) { HashSet<int> hashSet = new HashSet<int>(); for (int i = 0; i < list.Count; i++) { object? obj = list[i]; Component val = (Component)((obj is Component) ? obj : null); if ((Object)(object)val != (Object)null) { hashSet.Add(((Object)val).GetInstanceID()); } } return hashSet; } } internal sealed class HeadPitch : Module { internal static ConfigEntry<float> s_offset; private ConfigEntry<bool> m_log; private bool m_resolved; private Type tAnimEvent; private float m_nextTick; private Object m_lastPlayer; private HeadPitchLook m_attached; internal static Func<object, Vector3> s_headLookDir; internal static Func<object, Transform> s_head; internal static Func<object, bool> s_headRotation; internal static Func<object, float> s_lookWeight; internal static Func<object, float> s_headLookWeight; public override string Title => "Head Pitch"; protected override string Section => "10 - Head Pitch"; protected override bool DefaultOn => false; protected override string Description => "Raise your character's head by a set angle. The head follows where the camera looks, and the camera sits behind and above, so it tends to look down. Only your own screen changes. Off by default."; protected override void BindSettings(ConfigFile cfg) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown s_offset = cfg.Bind<float>(Section, "Head pitch offset", 10f, new ConfigDescription("Degrees the head is raised on top of the camera-following look (about; the head's own angle). Negative lowers it. 0 = vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-30f, 30f), new object[0])); m_log = cfg.Bind<bool>(Section, "Log", false, "Log when the offset is attached to your character."); } private static Func<object, T> Getter<T>(FieldInfo f) { try { ParameterExpression parameterExpression = Expression.Parameter(typeof(object), "o"); Expression expression = Expression.Field(Expression.Convert(parameterExpression, f.DeclaringType), f); if (f.FieldType != typeof(T)) { expression = Expression.Convert(expression, typeof(T)); } return Expression.Lambda<Func<object, T>>(expression, new ParameterExpression[1] { parameterExpression }).Compile(); } catch (Exception) { return (object x) => (T)f.GetValue(x); } } private bool Resolve() { if (m_resolved) { return true; } tAnimEvent = R.Type("CharacterAnimEvent"); if (tAnimEvent == null) { return false; } FieldInfo fieldInfo = R.Field(tAnimEvent, "m_headLookDir"); FieldInfo fieldInfo2 = R.Field(tAnimEvent, "m_head"); FieldInfo fieldInfo3 = R.Field(tAnimEvent, "m_headRotation"); FieldInfo fieldInfo4 = R.Field(tAnimEvent, "m_lookWeight"); FieldInfo fieldInfo5 = R.Field(tAnimEvent, "m_headLookWeight"); string text = R.Missing(fieldInfo, "CharacterAnimEvent.m_headLookDir", fieldInfo2, "CharacterAnimEvent.m_head", fieldInfo3, "CharacterAnimEvent.m_headRotation", fieldInfo4, "CharacterAnimEvent.m_lookWeight", fieldInfo5, "CharacterAnimEvent.m_headLookWeight"); if (text == null && (fieldInfo.FieldType != typeof(Vector3) || !typeof(Transform).IsAssignableFrom(fieldInfo2.FieldType) || fieldInfo3.FieldType != typeof(bool) || fieldInfo4.FieldType != typeof(float) || fieldInfo5.FieldType != typeof(float))) { text = "CharacterAnimEvent field types"; } if (text != null) { Broken(text); return false; } s_headLookDir = Getter<Vector3>(fieldInfo); s_head = Getter<Transform>(fieldInfo2); s_headRotation = Getter<bool>(fieldInfo3); s_lookWeight = Getter<float>(fieldInfo4); s_headLookWeight = Getter<float>(fieldInfo5); m_resolved = true; return true; } protected override void OnDisable() { if ((Object)(object)m_attached != (Object)null) { Object.Destroy((Object)(object)m_attached); } m_attached = null; m_lastPlayer = null; } protected override void Run() { float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < m_nextTick) { return; } m_nextTick = realtimeSinceStartup + 0.5f; if (!Resolve()) { return; } Component val = R.LocalPlayer(); if ((Object)(object)val == (Object)null) { m_lastPlayer = null; m_attached = null; } else { if (object.ReferenceEquals(val, m_lastPlayer) && (Object)(object)m_attached != (Object)null) { return; } Component componentInChildren = val.GetComponentInChildren(tAnimEvent, true); if ((Object)(object)componentInChildren == (Object)null) { return; } m_lastPlayer = (Object)(object)val; GameObject gameObject = componentInChildren.gameObject; if ((Object)(object)gameObject.GetComponent<Animator>() == (Object)null) { Broken("the player's CharacterAnimEvent is not next to its Animator"); return; } HeadPitchLook headPitchLook = gameObject.GetComponent<HeadPitchLook>(); if ((Object)(object)headPitchLook == (Object)null) { headPitchLook = gameObject.AddComponent<HeadPitchLook>(); } headPitchLook.m_event = componentInChildren; m_attached = headPitchLook; if (m_log.Value) { Plugin.Log.LogInfo((object)("Head Pitch: attached to your character, offset " + s_offset.Value.ToString("0.#", CultureInfo.InvariantCulture) + " deg.")); } } } } public sealed class HeadPitchLook : MonoBehaviour { internal Component m_event; private Animator m_animator; private bool m_reported; private void Awake() { m_animator = ((Component)this).GetComponent<Animator>(); } private void OnAnimatorIK(int layerIndex) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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_013a: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)m_event == (Object)null || (Object)(object)m_animator == (Object)null || HeadPitch.s_offset == null) { return; } float value = HeadPitch.s_offset.Value; if (value == 0f) { return; } try { object arg = m_event; if (!HeadPitch.s_headRotation(arg)) { return; } Transform val = HeadPitch.s_head(arg); if ((Object)(object)val == (Object)null) { return; } Vector3 val2 = HeadPitch.s_headLookDir(arg); if (val2 == Vector3.zero) { return; } float num = Mathf.Sqrt(val2.x * val2.x + val2.z * val2.z); if (!(num < 0.0001f)) { float num2 = HeadPitch.s_lookWeight(arg) * HeadPitch.s_headLookWeight(arg); float num3 = Mathf.Atan2(val2.y, num) * 57.29578f + value / Mathf.Max(num2, 0.1f); if (num3 > 85f) { num3 = 85f; } else if (num3 < -85f) { num3 = -85f; } float num4 = num3 * ((float)Math.PI / 180f); float num5 = Mathf.Cos(num4) / num; m_animator.SetLookAtPosition(val.position + new Vector3(val2.x * num5, Mathf.Sin(num4), val2.z * num5) * 10f); } } catch (Exception ex) { if (!m_reported) { m_reported = true; Plugin.Log.LogWarning((object)("Head Pitch: " + ex)); } } } } internal sealed class OvenDoor : Module { private const string OvenPrefab = "piece_oven"; private ConfigEntry<float> m_radius; private ConfigEntry<bool> m_log; private bool m_resolved; private Type tScene; private Type tProximity; private FieldInfo fAllPieces; private MethodInfo mGetPrefab; private float m_nextTick; private Object m_scene; private float m_applied = -1f; private float m_vanillaRadius = -1f; public override string Title => "Oven Door"; protected override string Section => "11 - Oven Door"; protected override bool DefaultOn => false; protected override string Description => "The stone oven's doors open only when you come within \"Open radius\" of them, instead of 2 m. Only your own screen changes. Off by default."; protected override void BindSettings(ConfigFile cfg) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown m_radius = cfg.Bind<float>(Section, "Open radius", 0.5f, new ConfigDescription("The doors open while your character touches a sphere of this radius (m) around the door. The game's own is 2. 0.05 keeps them shut in practice.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 5f), new object[0])); m_log = cfg.Bind<bool>(Section, "Log", false, "Log when the radius is applied, with the number of loaded ovens."); } private bool Resolve() { if (m_resolved) { return true; } tScene = R.Type("ZNetScene"); Type type = R.Type("Piece"); tProximity = R.Type("ProximityState"); if (tScene == null || type == null || tProximity == null) { return false; } fAllPieces = R.Field(type, "s_allPieces"); mGetPrefab = R.Method(tScene, "GetPrefab", typeof(string)); string text = R.Missing(fAllPieces, "Piece.s_allPieces", mGetPrefab, "ZNetScene.GetPrefab(string)"); if (text != null) { Broken(text); return false; } m_resolved = true; return true; } protected override void OnDisable() { if (m_resolved && m_vanillaRadius > 0f) { object obj = R.Instance(tScene); if ((Object)((obj is Object) ? obj : null) != (Object)null) { Apply(obj, m_vanillaRadius, on: false); } } m_scene = null; m_applied = -1f; } protected override void Run() { float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < m_nextTick) { return; } m_nextTick = realtimeSinceStartup + 0.5f; if (!Resolve()) { return; } object obj = R.Instance(tScene); Object val = (Object)((obj is Object) ? obj : null); if (val == (Object)null) { m_scene = null; return; } float value = m_radius.Value; if ((!object.ReferenceEquals(val, m_scene) || value != m_applied) && Apply(val, value, on: true)) { m_scene = val; m_applied = value; } } private bool Apply(object scene, float radius, bool on) { object? obj = mGetPrefab.Invoke(scene, new object[1] { "piece_oven" }); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if ((Object)(object)val == (Object)null) { return false; } if (m_vanillaRadius < 0f) { SphereCollider val2 = Sphere(val); if ((Object)(object)val2 == (Object)null) { Broken("the oven's ProximityState trigger sphere"); return false; } m_vanillaRadius = val2.radius; } Set(val, radius); int num = 0; if (fAllPieces.GetValue(null) is IList list) { for (int i = 0; i < list.Count; i++) { object? obj2 = list[i]; Component val3 = (Component)((obj2 is Component) ? obj2 : null); if (!((Object)(object)val3 == (Object)null)) { string name = ((Object)val3.gameObject).name; if ((!(name != "piece_oven") || name.StartsWith("piece_oven(", StringComparison.Ordinal)) && Set(val3.gameObject, radius)) { num++; } } } } if (m_log.Value) { Plugin.Log.LogInfo((object)("Oven Door: radius " + radius.ToString("0.##", CultureInfo.InvariantCulture) + " m" + (on ? "" : " (the game's own)") + " on the prefab and " + num + " loaded oven(s).")); } return true; } private SphereCollider Sphere(GameObject oven) { Component componentInChildren = oven.GetComponentInChildren(tProximity, true); if ((Object)(object)componentInChildren == (Object)null) { return null; } SphereCollider component = componentInChildren.GetComponent<SphereCollider>(); if (!((Object)(object)component != (Object)null) || !((Collider)component).isTrigger) { return null; } return component; } private bool Set(GameObject oven, float radius) { SphereCollider val = Sphere(oven); if ((Object)(object)val == (Object)null) { return false; } if (val.radius != radius) { val.radius = radius; } return true; } } [BepInPlugin("mage38.vanillafix", "Vanilla Fix", "1.2.0")] public class Plugin : BaseUnityPlugin { public const string Guid = "mage38.vanillafix"; public const string Name = "Vanilla Fix"; public const string Version = "1.2.0"; internal static ManualLogSource Log; private readonly List<Module> m_modules = new List<Module>(); private string m_configPath; private DateTime m_configStamp; private float m_nextConfigCheck; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; m_modules.Add(new UnloadAfterTravel()); m_modules.Add(new CampFires()); m_modules.Add(new GroundItemMerging()); m_modules.Add(new StationRange()); m_modules.Add(new WaveDepth()); m_modules.Add(new SeaExploring()); m_modules.Add(new TombExit()); m_modules.Add(new FireWarmth()); m_modules.Add(new TravelPause()); m_modules.Add(new HeadPitch()); m_modules.Add(new OvenDoor()); RenumberOldSections(); for (int i = 0; i < m_modules.Count; i++) { m_modules[i].Bind(((BaseUnityPlugin)this).Config); } m_configPath = ((BaseUnityPlugin)this).Config.ConfigFilePath; m_configStamp = Stamp(m_configPath); Log.LogInfo((object)("Vanilla Fix 1.2.0 ready. Modules on: " + EnabledList())); } private string EnabledList() { List<string> list = new List<string>(); for (int i = 0; i < m_modules.Count; i++) { if (m_modules[i].Enabled) { list.Add(m_modules[i].Title); } } if (list.Count != 0) { return string.Join(", ", list.ToArray()); } return "none"; } private void RenumberOldSections() { try { string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; if (File.Exists(configFilePath)) { string text = File.ReadAllText(configFilePath); List<string> list = new List<string>(); for (int i = 0; i < m_modules.Count; i++) { list.Add(m_modules[i].SectionName); } int renamed; string contents = ConfigSections.Renumber(text, list, out renamed); if (renamed != 0) { File.WriteAllText(configFilePath, contents, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); ((BaseUnityPlugin)this).Config.Reload(); Log.LogInfo((object)("Renamed " + renamed + " config sections from 1.0.0 to two-digit numbers (01, 02, ...); settings kept.")); } } } catch (Exception ex) { Log.LogWarning((object)("Could not rename the old config sections: " + ex.Message)); } } private static DateTime Stamp(string path) { try { return File.GetLastWriteTimeUtc(path); } catch (Exception) { return DateTime.MinValue; } } private void Update() { float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup >= m_nextConfigCheck) { m_nextConfigCheck = realtimeSinceStartup + 5f; DateTime dateTime = Stamp(m_configPath); if (dateTime != m_configStamp) { m_configStamp = dateTime; try { ((BaseUnityPlugin)this).Config.Reload(); Log.LogInfo((object)("Config reloaded. Modules on: " + EnabledList())); } catch (Exception ex) { Log.LogWarning((object)("Could not reload the config: " + ex.Message)); } } } for (int i = 0; i < m_modules.Count; i++) { m_modules[i].Tick(); } } private void FixedUpdate() { for (int i = 0; i < m_modules.Count; i++) { m_modules[i].FixedTick(); } } private void OnDestroy() { for (int i = 0; i < m_modules.Count; i++) { m_modules[i].Shutdown(); } } } internal sealed class SeaExploring : Module { private const int Ocean = 256; private const float SeaLevel = 30f; private ConfigEntry<float> m_radius; private ConfigEntry<float> m_fogRadius; private ConfigEntry<float> m_interval; private ConfigEntry<string> m_fogWeathers; private ConfigEntry<bool> m_log; private bool m_resolved; private Type tMinimap; private Type tWorldGen; private FieldInfo fExploreRadius; private FieldInfo fExplored; private FieldInfo fPixelSize; private FieldInfo fTextureSize; private FieldInfo fFogTexture; private MethodInfo mExplore; private MethodInfo mGetLocalShip; private MethodInfo mGetHeight; private MethodInfo mGetBiome; private MethodInfo mCurrentEnv; private FieldInfo fEnvName; private string m_fogKey; private readonly HashSet<string> m_fogNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private object m_map; private Func<int, int, bool> m_explore; private BitArray m_notWater; private bool m_overriding; private float m_savedRadius; private float m_nextTick; private int m_lastFogState = -1; public override string Title => "Sea Exploring"; protected override string Section => "06 - Sea Exploring"; protected override bool DefaultOn => false; protected override string Description => "While on a ship, reveal open water on the minimap within a larger radius, and reveal less than usual in every direction in foggy weather. Off by default."; protected override void BindSettings(ConfigFile cfg) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown m_radius = cfg.Bind<float>(Section, "Explore radius at sea", 80f, new ConfigDescription("Radius (m) within which open water is revealed while on a ship. The game's own radius is 50 m.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), new object[0])); m_fogWeathers = cfg.Bind<string>(Section, "Fog weathers", "Misty", "Weather (environment) names that count as fog, comma separated."); m_fogRadius = cfg.Bind<float>(Section, "Explore radius in fog", 20f, new ConfigDescription("The game's explore radius (m), in every direction, while on a ship in foggy weather. Nothing extra is revealed then.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), new object[0])); m_interval = cfg.Bind<float>(Section, "Check interval", 1f, new ConfigDescription("Seconds between updates.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 10f), new object[0])); m_log = cfg.Bind<bool>(Section, "Log", false, "Log when foggy weather starts or ends while on a ship."); } private bool Resolve() { if (m_resolved) { return true; } tMinimap = R.Type("Minimap"); tWorldGen = R.Type("WorldGenerator"); if (tMinimap == null || tWorldGen == null) { return false; } fExploreRadius = R.Field(tMinimap, "m_exploreRadius"); fExplored = R.Field(tMinimap, "m_explored"); fPixelSize = R.Field(tMinimap, "m_pixelSize"); fTextureSize = R.Field(tMinimap, "m_textureSize"); fFogTexture = R.Field(tMinimap, "m_fogTexture"); mExplore = R.Method(tMinimap, "Explore", typeof(int), typeof(int)); mGetLocalShip = R.Method(R.Type("Ship"), "GetLocalShip"); mGetHeight = R.Method(tWorldGen, "GetHeight", typeof(float), typeof(float)); mGetBiome = R.Method(tWorldGen, "GetBiome", typeof(Vector3)); mCurrentEnv = R.Method(R.Type("EnvMan"), "GetCurrentEnvironment"); fEnvName = R.Field(R.Type("EnvSetup"), "m_name"); string text = R.Missing(fExploreRadius, "Minimap.m_exploreRadius", fExplored, "Minimap.m_explored", fPixelSize, "Minimap.m_pixelSize", fTextureSize, "Minimap.m_textureSize", fFogTexture, "Minimap.m_fogTexture", mExplore, "Minimap.Explore(int, int)", mGetLocalShip, "Ship.GetLocalShip", mGetHeight, "WorldGenerator.GetHeight", mGetBiome, "WorldGenerator.GetBiome", mCurrentEnv, "EnvMan.GetCurrentEnvironment", fEnvName, "EnvSetup.m_name"); if (text != null) { Broken(text); return false; } m_resolved = true; return true; } private void Restore() { if (m_overriding) { m_overriding = false; object map = m_map; if ((Object)((map is Object) ? map : null) != (Object)null) { fExploreRadius.SetValue(m_map, m_savedRadius); } } } protected override void OnDisable() { if (m_resolved) { Restore(); } } protected override void Run() { //IL_0240: Unknown result type (might be due to invalid IL or missing references) float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < m_nextTick) { return; } m_nextTick = realtimeSinceStartup + m_interval.Value; if (!Resolve()) { return; } object obj = R.Instance(tMinimap); if ((Object)((obj is Object) ? obj : null) == (Object)null) { m_map = null; m_overriding = false; return; } if (!object.ReferenceEquals(obj, m_map)) { m_map = obj; m_overriding = false; m_notWater = null; try { m_explore = (Func<int, int, bool>)Delegate.CreateDelegate(typeof(Func<int, int, bool>), obj, mExplore); } catch (Exception) { MethodInfo mi = mExplore; object target = obj; m_explore = (int x, int y) => (bool)mi.Invoke(target, new object[2] { x, y }); } } Component val = R.LocalPlayer(); bool flag = (Object)(object)val != (Object)null && mGetLocalShip.Invoke(null, null) != null; string text = (flag ? CurrentWeather() : null); bool flag2 = text != null && m_fogNames.Contains(text); int num = ((!flag) ? (-1) : (flag2 ? 1 : 0)); if (m_log.Value && num != m_lastFogState && num >= 0) { Plugin.Log.LogInfo((object)("Sea Exploring: weather '" + text + "', " + (flag2 ? "explore radius " : "explore radius at sea ") + (flag2 ? m_fogRadius.Value : m_radius.Value).ToString("0", CultureInfo.InvariantCulture) + " m")); } m_lastFogState = num; if (flag2) { if (!m_overriding) { m_savedRadius = Convert.ToSingle(fExploreRadius.GetValue(obj)); m_overriding = true; } fExploreRadius.SetValue(obj, m_fogRadius.Value); } else { Restore(); if (flag && !(m_radius.Value <= 0f)) { RevealWater(obj, val.transform.position); } } } private string CurrentWeather() { string text = m_fogWeathers.Value ?? ""; if (text != m_fogKey) { m_fogKey = text; m_fogNames.Clear(); string[] array = text.Split(','); foreach (string text2 in array) { string text3 = text2.Trim(); if (text3.Length > 0) { m_fogNames.Add(text3); } } } object obj = R.Instance(R.Type("EnvMan")); if (obj == null) { return null; } object obj2 = mCurrentEnv.Invoke(obj, null); if (obj2 == null) { return null; } return fEnvName.GetValue(obj2) as string; } private void RevealWater(object map, Vector3 at) { //IL_0172: Unknown result type (might be due to invalid IL or missing references) float num = Convert.ToSingle(fPixelSize.GetValue(map)); int num2 = Convert.ToInt32(fTextureSize.GetValue(map)); BitArray bitArray = fExplored.GetValue(map) as BitArray; object obj = R.Instance(tWorldGen); if (num <= 0f || num2 <= 0 || bitArray == null || obj == null) { return; } if (m_notWater == null || m_notWater.Length != num2 * num2) { m_notWater = new BitArray(num2 * num2); } int num3 = num2 / 2; int num4 = Mathf.RoundToInt(at.x / num + (float)num3); int num5 = Mathf.RoundToInt(at.z / num + (float)num3); int num6 = Mathf.CeilToInt(m_radius.Value / num); bool flag = false; object[] array = new object[2]; object[] array2 = new object[1]; for (int i = num5 - num6; i <= num5 + num6; i++) { if (i < 0 || i >= num2) { continue; } for (int j = num4 - num6; j <= num4 + num6; j++) { if (j < 0 || j >= num2) { continue; } int num7 = j - num4; int num8 = i - num5; if (num7 * num7 + num8 * num8 > num6 * num6) { continue; } int index = i * num2 + j; if (!bitArray.Get(index) && !m_notWater.Get(index)) { float num9 = (float)(j - num3) * num; float num10 = (float)(i - num3) * num; array2[0] = (object)new Vector3(num9, 0f, num10); bool flag2 = Convert.ToInt32(mGetBiome.Invoke(obj, array2)) == 256; if (!flag2) { array[0] = num9; array[1] = num10; flag2 = Convert.ToSingle(mGetHeight.Invoke(obj, array)) <= 30f; } if (!flag2) { m_notWater.Set(index, value: true); } else if (m_explore(j, i)) { flag = true; } } } } if (flag) { object? value = fFogTexture.GetValue(map); Texture2D val = (Texture2D)((value is Texture2D) ? value : null); if ((Object)(object)val != (Object)null) { val.Apply(); } } } } internal sealed class StationRange : Module { private ConfigEntry<float> m_interval; private ConfigEntry<bool> m_log; private bool m_resolved; private FieldInfo fAllStations; private FieldInfo fCollider; private MethodInfo mGetExtensions; private float m_nextTick; public override string Title => "Station Range"; protected override string Section => "04 - Station Range"; protected override bool DefaultOn => true; protected override string Description => "A freshly loaded workbench, forge or artisan table protects only 20 m against spawns until you walk up to it. Apply its full radius (extensions included) as soon as it is loaded."; protected override void BindSettings(ConfigFile cfg) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown m_interval = cfg.Bind<float>(Section, "Check interval", 2f, new ConfigDescription("Seconds between passes. The game recomputes a station at most every 2 s anyway.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 30f), new object[0])); m_log = cfg.Bind<bool>(Section, "Log", false, "Log when a station's player-base radius changes."); } private bool Resolve() { if (m_resolved) { return true; } Type type = R.Type("CraftingStation"); if (type == null) { return false; } fAllStations = R.Field(type, "m_allStations"); fCollider = R.Field(type, "m_effectAreaCollider"); mGetExtensions = R.Method(type, "GetExtensions"); string text = R.Missing(fAllStations, "CraftingStation.m_allStations", fCollider, "CraftingStation.m_effectAreaCollider", mGetExtensions, "CraftingStation.GetExtensions"); if (text != null) { Broken(text); return false; } m_resolved = true; return true; } private static float Radius(Collider c) { SphereCollider val = (SphereCollider)(object)((c is SphereCollider) ? c : null); if ((Object)(object)val != (Object)null) { return val.radius; } CapsuleCollider val2 = (CapsuleCollider)(object)((c is CapsuleCollider) ? c : null); if (!((Object)(object)val2 != (Object)null)) { return -1f; } return val2.radius; } protected override void Run() { //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < m_nextTick) { return; } m_nextTick = realtimeSinceStartup + m_interval.Value; if (!Resolve() || !(fAllStations.GetValue(null) is IList { Count: not 0 } list)) { return; } object[] array = new object[list.Count]; list.CopyTo(array, 0); foreach (object obj in array) { Component val = (Component)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null) { continue; } object? value = fCollider.GetValue(val); Collider val2 = (Collider)((value is Collider) ? value : null); if ((Object)(object)val2 == (Object)null) { continue; } float num = Radius(val2); mGetExtensions.Invoke(val, null); if (m_log.Value) { float num2 = Radius(val2); if (!(Mathf.Abs(num2 - num) < 0.01f)) { Vector3 position = val.transform.position; Plugin.Log.LogInfo((object)("Station Range: " + R.PrefabName(val.gameObject) + " (" + position.x.ToString("0.0", CultureInfo.InvariantCulture) + ", " + position.z.ToString("0.0", CultureInfo.InvariantCulture) + "): player-base radius " + num.ToString("0.#", CultureInfo.InvariantCulture) + " -> " + num2.ToString("0.#", CultureInfo.InvariantCulture) + " m")); } } } } } internal sealed class TombExit : Module { private const float Forward = 1f; private ConfigEntry<string> m_locations; private ConfigEntry<float> m_interval; private ConfigEntry<bool> m_log; private bool m_resolved; private Type tTeleport; private FieldInfo fAllLocations; private FieldInfo fInterior; private FieldInfo fCustom; private readonly HashSet<int> m_seen = new HashSet<int>(); private readonly List<Transform> m_moved = new List<Transform>(); private HashSet<string> m_wanted; private string m_wantedKey; private float m_nextTick; public override string Title => "Tomb Exit"; protected override string Section => "07 - Tomb Exit"; protected override bool DefaultOn => true; protected override string Description => "The exit of the Smouldering Tomb (Hildir's quest) sits 1 m too deep: from inside there is no prompt and you cannot walk out. Move it to where the exit is in every other dungeon of this kind."; protected override void BindSettings(ConfigFile cfg) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown m_locations = cfg.Bind<string>(Section, "Locations", "Hildir_crypt", "Location prefab names whose exit is checked (comma separated). An exit is only moved while it sits on the origin of a zone-aligned interior, which in the unmodified game is the case for the Smouldering Tomb only."); m_interval = cfg.Bind<float>(Section, "Check interval", 2f, new ConfigDescription("Seconds between looks at the loaded locations.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 30f), new object[0])); m_log = cfg.Bind<bool>(Section, "Log", false, "Log when an exit is moved."); } private bool Resolve() { if (m_resolved) { return true; } Type type = R.Type("Location"); if (type == null) { return false; } tTeleport = R.Type("Teleport"); fAllLocations = R.Field(type, "s_allLocations"); fInterior = R.Field(type, "m_interiorTransform"); fCustom = R.Field(type, "m_useCustomInteriorTransform"); string text = R.Missing(fAllLocations, "Location.s_allLocations", fInterior, "Location.m_interiorTransform", fCustom, "Location.m_useCustomInteriorTransform", tTeleport, "Teleport"); if (text != null) { Broken(text); return false; } m_resolved = true; return true; } private void ParseLocations() { string text = m_locations.Value ?? ""; if (text == m_wantedKey && m_wanted != null) { return; } m_wantedKey = text; m_wanted = new HashSet<string>(StringComparer.Ordinal); string[] array = text.Split(','); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length > 0) { m_wanted.Add(text2); } } m_seen.Clear(); } protected override void Run() { //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < m_nextTick) { return; } m_nextTick = realtimeSinceStartup + m_interval.Value; if (!Resolve()) { return; } ParseLocations(); if (!(fAllLocations.GetValue(null) is IList { Count: not 0 } list)) { return; } if (m_seen.Count > 4096) { m_seen.Clear(); } for (int i = 0; i < list.Count; i++) { object? obj = list[i]; Component val = (Component)((obj is Component) ? obj : null); if ((Object)(object)val == (Object)null || !m_seen.Add(((Object)val).GetInstanceID()) || !m_wanted.Contains(R.PrefabName(val.gameObject)) || !(bool)fCustom.GetValue(val)) { continue; } object? value = fInterior.GetValue(val); Transform val2 = (Transform)((value is Transform) ? value : null); if ((Object)(object)val2 == (Object)null) { continue; } Component[] componentsInChildren = ((Component)val2).GetComponentsInChildren(tTeleport, true); for (int j = 0; j < componentsInChildren.Length; j++) { Transform transform = componentsInChildren[j].transform; if ((Object)(object)transform.parent != (Object)(object)val2) { continue; } Vector3 localPosition = transform.localPosition; if (!(Mathf.Abs(localPosition.x) > 0.01f) && !(Mathf.Abs(localPosition.z) > 0.01f)) { transform.localPosition = localPosition + transform.localRotation * Vector3.forward * 1f; m_moved.Add(transform); if (m_log.Value) { Vector3 position = val.transform.position; Plugin.Log.LogInfo((object)("Tomb Exit: " + R.PrefabName(val.gameObject) + " (" + position.x.ToString("0", CultureInfo.InvariantCulture) + ", " + position.z.ToString("0", CultureInfo.InvariantCulture) + "): exit moved " + 1f.ToString("0.#", CultureInfo.InvariantCulture) + " m into the entrance room")); } } } } if (m_moved.Count > 64) { m_moved.RemoveAll((Transform t) => (Object)(object)t == (Object)null); } } protected override void OnDisable() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_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) for (int i = 0; i < m_moved.Count; i++) { Transform val = m_moved[i]; if (!((Object)(object)val == (Object)null)) { val.localPosition -= val.localRotation * Vector3.forward * 1f; } } m_moved.Clear(); m_seen.Clear(); } } internal sealed class TravelPause : Module { private sealed class Held { public object Effect; public float Time; public FieldInfo[] Fields; public float[] Values; } private static readonly string[] s_workLeft = new string[4] { "m_damageLeft", "m_fireDamageLeft", "m_spiritDamageLeft", "m_healthOverTimeTicks" }; private ConfigEntry<bool> m_harmful; private ConfigEntry<bool> m_cooldown; private ConfigEntry<bool> m_log; private bool m_resolved; private Func<object, bool> m_isTeleporting; private MethodInfo mTeleporting; private MethodInfo mGetHealth; private MethodInfo mSetHealth; private FieldInfo fSeman; private FieldInfo fEffects; private FieldInfo fTtl; private FieldInfo fTime; private FieldInfo fStamina; private FieldInfo fEitr; private FieldInfo fCooldown; private readonly Dictionary<Type, FieldInfo[]> m_workFields = new Dictionary<Type, FieldInfo[]>(); private Component m_player; private readonly List<Held> m_held = new List<Held>(); private float m_health; private float m_stamina; private float m_eitr; private float m_cooldownValue; private float m_startedAt; public override string Title => "Travel Pause"; protected override string Section => "09 - Travel Pause"; protected override bool DefaultOn => true; protected override string Description => "While a portal or dungeon loads, the game already holds your food timers and regeneration, but meads, Rested and the Forsaken power keep counting down. Hold those still as well, so a slow load costs nothing."; protected override void BindSettings(ConfigFile cfg) { m_harmful = cfg.Bind<bool>(Section, "Hold harmful effects", true, "Also hold poison and burning, which otherwise run out harmlessly during the load. Off: they keep running as in the vanilla game."); m_cooldown = cfg.Bind<bool>(Section, "Hold Forsaken power cooldown", true, "Also hold the cooldown of the Forsaken power."); m_log = cfg.Bind<bool>(Section, "Log", true, "Log, after each teleport, how long the timers were held and how many effects."); } private bool Resolve() { if (m_resolved) { return true; } Type type = R.Type("Player"); Type type2 = R.Type("Character"); Type type3 = R.Type("SEMan"); Type type4 = R.Type("StatusEffect"); if (type == null || type2 == null || type3 == null || type4 == null) { return false; } mTeleporting = R.Method(type, "IsTeleporting"); mGetHealth = R.Method(type2, "GetHealth"); mSetHealth = R.Method(type2, "SetHealth", typeof(float)); fSeman = R.Field(type2, "m_seman"); fEffects = R.Field(type3, "m_statusEffects"); fTtl = R.Field(type4, "m_ttl"); fTime = R.Field(type4, "m_time"); fStamina = R.Field(type, "m_stamina"); fEitr = R.Field(type, "m_eitr"); fCooldown = R.Field(type, "m_guardianPowerCooldown"); string text = R.Missing(mTeleporting, "Player.IsTeleporting", mGetHealth, "Character.GetHealth", mSetHealth, "Character.SetHealth", fSeman, "Character.m_seman", fEffects, "SEMan.m_statusEffects", fTtl, "StatusEffect.m_ttl", fTime, "StatusEffect.m_time", fStamina, "Player.m_stamina", fEitr, "Player.m_eitr", fCooldown, "Player.m_guardianPowerCooldown"); if (text != null) { Broken(text); return false; } try { ParameterExpression parameterExpression = Expression.Parameter(typeof(object), "o"); m_isTeleporting = Expression.Lambda<Func<object, bool>>(Expression.Call(Expression.Convert(parameterExpression, type), mTeleporting), new ParameterExpression[1] { parameterExpression }).Compile(); } catch (Exception) { MethodInfo m = mTeleporting; m_isTeleporting = (object x) => (bool)m.Invoke(x, null); } m_resolved = true; return true; } private FieldInfo[] WorkFields(Type t) { if (m_workFields.TryGetValue(t, out var value)) { return value; } List<FieldInfo> list = new List<FieldInfo>(); for (int i = 0; i < s_workLeft.Length; i++) { Type type = t; while (type != null && type != typeof(ScriptableObject)) { FieldInfo field = type.GetField(s_workLeft[i], BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType == typeof(float)) { list.Add(field); break; } type = type.BaseType; } } value = list.ToArray(); m_workFields[t] = value; return value; } private static bool IsHarmful(Type t) { Type type = t; while (type != null) { if (type.Name == "SE_Poison" || type.Name == "SE_Burning") { return true; } type = type.BaseType; } return false; } protected override void Run() { Step(); } protected override void FixedRun() { Step(); } private void Step() { if (!Resolve()) { return; } Component val = R.LocalPlayer(); bool flag = (Object)(object)val != (Object)null && m_isTeleporting(val); if ((Object)(object)m_player != (Object)null && ((Object)(object)val != (Object)(object)m_player || !flag)) { Finish((Object)(object)val == (Object)(object)m_player); } else if (flag) { if ((Object)(object)m_player == (Object)null) { Take(val); } else { Restore(); } } } private IList Effects(Component player) { object value = fSeman.GetValue(player); if (value == null) { return null; } return fEffects.GetValue(value) as IList; } private void Take(Component player) { m_player = player; m_held.Clear(); m_startedAt = Time.realtimeSinceStartup; m_health = (float)mGetHealth.Invoke(player, null); m_stamina = (float)fStamina.GetValue(player); m_eitr = (float)fEitr.GetValue(player); m_cooldownValue = (float)fCooldown.GetValue(player); IList list = Effects(player); if (list == null) { return; } for (int i = 0; i < list.Count; i++) { object obj = list[i]; if (obj == null) { continue; } Type type = obj.GetType(); if (!m_harmful.Value && IsHarmful(type)) { continue; } FieldInfo[] array = WorkFields(type); if (!((float)fTtl.GetValue(obj) <= 0f) || array.Length != 0) { Held held = new Held(); held.Effect = obj; held.Time = (float)fTime.GetValue(obj); held.Fields = array; held.Values = new float[array.Length]; for (int j = 0; j < array.Length; j++) { held.Values[j] = (float)array[j].GetValue(obj); } m_held.Add(held); } } } private void Restore() { IList list = Effects(m_player); for (int i = 0; i < m_held.Count; i++) { Held held = m_held[i]; if (list != null && list.Contains(held.Effect)) { fTime.SetValue(held.Effect, held.Time); for (int j = 0; j < held.Fields.Length; j++) { held.Fields[j].SetValue(held.Effect, held.Values[j]); } } } if (Mathf.Abs((float)mGetHealth.Invoke(m_player, null) - m_health) > 0.001f) { mSetHealth.Invoke(m_player, new object[1] { m_health }); } if (Mathf.Abs((float)fStamina.GetValue(m_player) - m_stamina) > 0.001f) { fStamina.SetValue(m_player, m_stamina); } if (Mathf.Abs((float)fEitr.GetValue(m_player) - m_eitr) > 0.001f) { fEitr.SetValue(m_player, m_eitr); } if (m_cooldown.Value && Mathf.Abs((float)fCooldown.GetValue(m_player) - m_cooldownValue) > 0.001f) { fCooldown.SetValue(m_player, m_cooldownValue); } } private void Finish(bool samePlayer) { if (samePlayer && m_log.Value) { float num = Time.realtimeSinceStartup - m_startedAt; Plugin.Log.LogInfo((object)("Travel Pause: held " + m_held.Count + " status effect(s)" + (m_cooldown.Value ? " and the Forsaken power cooldown" : "") + " for " + num.ToString("0.0", CultureInfo.InvariantCulture) + " s of loading.")); } m_player = null; m_held.Clear(); } protected override void OnDisable() { m_player = null; m_held.Clear(); } } internal sealed class UnloadAfterTravel : Module { private ConfigEntry<float> m_minTeleport; private ConfigEntry<float> m_boatDistance; private ConfigEntry<float> m_delay; private ConfigEntry<float> m_minInterval; private ConfigEntry<KeyCode> m_key; private ConfigEntry<bool> m_showMessage; private ConfigEntry<bool> m_log; private bool m_resolved; private FieldInfo fTeleporting; private FieldInfo fFrom; private FieldInfo fTarget; private MethodInfo mGameInstance; private MethodInfo mCollect; private MethodInfo mGetLocalShip; private MethodInfo mMessage; private Type tMessageType; private bool m_wasTeleporting; private float m_pendingAt = -1f; private string m_pendingReason; private Vector3 m_anchor; private bool m_haveAnchor; private float m_lastUnload = -9999f; private float m_nextPoll; private int m_watchFrames; private float m_worstFrame; private long m_callMs; private string m_lastReason; public override string Title => "Unload After Travel"; protected override string Section => "01 - Unload After Travel"; protected override bool DefaultOn => true; protected override string Description => "Free unused assets after teleports and long sea voyages. The game itself never does this after a teleport, so the textures of the place you left stay in video memory."; protected override void BindSettings(ConfigFile cfg) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Expected O, but got Unknown m_minTeleport = cfg.Bind<float>(Section, "Minimum teleport distance", 0f, new ConfigDescription("Unload only after teleports that cover at least this straight-line distance (m). 0 = after every teleport. Raise it if short portal hops inside your base unload too often.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20000f), new object[0])); m_boatDistance = cfg.Bind<float>(Section, "Ship voyage distance", 3000f, new ConfigDescription("While on a ship, unload when you are this far (straight line, m) from where the last unload happened. 0 = off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20000f), new object[0])); m_delay = cfg.Bind<float>(Section, "Delay after arrival", 5f, new ConfigDescription("Seconds to wait after a teleport ends, so the objects of the place you left are gone first.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), new object[0])); m_minInterval = cfg.Bind<float>(Section, "Minimum interval", 30f, new ConfigDescription("Never unload more often than this many seconds, whatever the trigger.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3600f), new object[0])); m_key = cfg.Bind<KeyCode>(Section, "Unload key", (KeyCode)0, "Optional key to unload by hand (the console command cr does the same)."); m_showMessage = cfg.Bind<bool>(Section, "Show message", false, "Show a short message on screen when assets are freed."); m_log = cfg.Bind<bool>(Section, "Log", true, "Log each unload with the length of the hitch it caused."); } private bool Resolve() { if (m_resolved) { return true; } Type type = R.Type("Player"); Type type2 = R.Type("Game"); if (type == null || type2 == null) { return false; } fTeleporting = R.Field(type, "m_teleporting"); fFrom = R.Field(type, "m_teleportFromPos"); fTarget = R.Field(type, "m_teleportTargetPos"); mGameInstance = type2.GetMethod("get_instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); mCollect = R.Method(type2, "CollectResources", typeof(bool)); mGetLocalShip = R.Method(R.Type("Ship"), "GetLocalShip"); Type type3 = R.Type("Character"); if (type3 != null) { MethodInfo[] methods = type3.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "Message" && methodInfo.GetParameters().Length == 5) { mMessage = methodInfo; tMessageType = methodInfo.GetParameters()[0].ParameterType; } } } string text = R.Missing(fTeleporting, "Player.m_teleporting", fFrom, "Player.m_teleportFromPos", fTarget, "Player.m_teleportTargetPos", mGameInstance, "Game.instance", mCollect, "Game.CollectResources", mGetLocalShip, "Ship.GetLocalShip"); if (text != null) { Broken(text); return false; } m_resolved = true; return true; } protected override void Run() { //IL_0015: 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_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) if (m_watchFrames > 0) { WatchHitch(); } bool flag = (int)m_key.Value != 0 && Input.GetKeyDown(m_key.Value); float realtimeSinceStartup = Time.realtimeSinceStartup; if ((!flag && realtimeSinceStartup < m_nextPoll && m_pendingAt < 0f) || !Resolve()) { return; } Component val = R.LocalPlayer(); if ((Object)(object)val == (Object)null) { m_wasTeleporting = false; m_haveAnchor = false; return; } if (flag) { Unload("key", val); return; } if (realtimeSinceStartup >= m_nextPoll) { m_nextPoll = realtimeSinceStartup + 0.25f; bool flag2 = (bool)fTeleporting.GetValue(val); if (m_wasTeleporting && !flag2) { Vector3 a = (Vector3)fFrom.GetValue(val); Vector3 b = (Vector3)fTarget.GetValue(val); float num = R.Flat(a, b); if (num >= m_minTeleport.Value) { m_pendingAt = realtimeSinceStartup + m_delay.Value; m_pendingReason = "teleport of " + num.ToString("0", CultureInfo.InvariantCulture) + " m"; } } m_wasTeleporting = flag2; if (flag2) { m_pendingAt = -1f; return; } Vector3 position = val.transform.position; if (!m_haveAnchor) { m_anchor = position; m_haveAnchor = true; } if (m_pendingAt < 0f && m_boatDistance.Value > 0f && mGetLocalShip.Invoke(null, null) != null) { float num2 = R.Flat(position, m_anchor); if (num2 >= m_boatDistance.Value) { m_pendingAt = realtimeSinceStartup; m_pendingReason = "ship voyage of " + num2.ToString("0", CultureInfo.InvariantCulture) + " m"; } } } if (m_pendingAt >= 0f && realtimeSinceStartup >= m_pendingAt) { if (realtimeSinceStartup - m_lastUnload < m_minInterval.Value) { m_pendingAt = m_lastUnload + m_minInterval.Value; return; } m_pendingAt = -1f; Unload(m_pendingReason, val); } } private void Unload(string reason, Component player) { //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) object obj = mGameInstance.Invoke(null, null); if (obj == null) { return; } Stopwatch stopwatch = Stopwatch.StartNew(); mCollect.Invoke(obj, new object[1] { false }); m_callMs = stopwatch.ElapsedMilliseconds; m_lastUnload = Time.realtimeSinceStartup; m_anchor = player.transform.position; m_haveAnchor = true; m_lastReason = reason; m_watchFrames = 30; m_worstFrame = 0f; if (!m_showMessage.Value || !(mMessage != null)) { return; } try { mMessage.Invoke(player, new object[5] { Enum.ToObject(tMessageType, 1), "Freed unused assets", 0, null, false }); } catch (Exception) { } } private void WatchHitch() { float num = Time.unscaledDeltaTime * 1000f; if (num > m_worstFrame) { m_worstFrame = num; } m_watchFrames--; if (m_watchFrames == 0 && m_log.Value) { Plugin.Log.LogInfo((object)("Unload After Travel: freed unused assets (" + m_lastReason + "), call " + m_callMs + " ms, longest frame after it " + m_worstFrame.ToString("0", CultureInfo.InvariantCulture) + " ms.")); } } } internal static class R { public const BindingFlags Inst = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; public const BindingFlags Stat = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static readonly Dictionary<string, Type> s_types = new Dictionary<string, Type>(StringComparer.Ordinal); public static Type Type(string name) { if (s_types.TryGetValue(name, out var value) && value != null) { return value; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { try { value = assemblies[i].GetType(name, throwOnError: false); } catch (Exception) { value = null; } if (value != null) { s_types[name] = value; return value; } } return null; } public static FieldInfo Field(Type t, string name) { if (!(t == null)) { return t.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } return null; } public static MethodInfo Method(Type t, string name, params Type[] args) { if (!(t == null)) { return t.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, args, null); } return null; } public static object Instance(Type t) { if (t == null) { return null; } PropertyInfo property = t.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { try { object value = property.GetValue(null, null); if (value != null) { return value; } } catch (Exception) { } } FieldInfo fieldInfo = t.GetField("s_instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? t.GetField("m_instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (!(fieldInfo != null)) { return null; } return fieldInfo.GetValue(null); } public static string Missing(params object[] pairs) { List<string> list = new List<string>(); for (int i = 0; i + 1 < pairs.Length; i += 2) { if (pairs[i] == null) { list.Add(pairs[i + 1] as string); } } if (list.Count != 0) { return string.Join(", ", list.ToArray()); } return null; } public static float Flat(Vector3 a, Vector3 b) { float num = a.x - b.x; float num2 = a.z - b.z; return Mathf.Sqrt(num * num + num2 * num2); } public static string PrefabName(GameObject go) { if ((Object)(object)go == (Object)null) { return ""; } string text = ((Object)go).name; int num = text.IndexOf('('); if (num > 0) { text = text.Substring(0, num); } return text.Trim(); } public static Component LocalPlayer() { Type type = Type("Player"); FieldInfo fieldInfo = ((type != null) ? type.GetField("m_localPlayer", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) : null); if (!(fieldInfo != null)) { return null; } object? value = fieldInfo.GetValue(null); return (Component)((value is Component) ? value : null); } } internal static class ConfigSections { public static string Renumber(string text, IList<string> sections, out int renamed) { renamed = 0; for (int i = 0; i < sections.Count; i++) { string text2 = sections[i]; if (text2 != null && text2.Length >= 2 && text2[0] == '0' && !HasHeader(text, text2)) { string str = text2.Substring(1); Regex regex = new Regex("^[ \\t]*\\[" + Regex.Escape(str) + "\\][ \\t]*(?=\\r?$)", RegexOptions.Multiline); if (regex.IsMatch(text)) { text = regex.Replace(text, "[" + text2 + "]", 1); renamed++; } } } return text; } private static bool HasHeader(string text, string section) { return new Regex("^[ \\t]*\\[" + Regex.Escape(section) + "\\][ \\t]*(?=\\r?$)", RegexOptions.Multiline).IsMatch(text); } } internal static class Zdo { public static Func<object, int> Prefab; public static Func<object, Vector3> Position; public static Func<object, int, float, float> GetFloat; public static Func<object, int, long, long> GetLong; public static Action<object, int, float> SetFloat; public static Action<object, int, long> SetLong; public static Func<object, bool> IsOwner; private static bool s_tried; private static bool s_ok; public static bool Ready() { if (s_tried) { return s_ok; } s_tried = true; Type type = R.Type("ZDO"); if (type == null) { s_tried = false; return false; } MethodInfo mPrefab = R.Method(type, "GetPrefab"); MethodInfo mPos = R.Method(type, "GetPosition"); MethodInfo mGetF = R.Method(type, "GetFloat", typeof(int), typeof(float)); MethodInfo mGetL = R.Method(type, "GetLong", typeof(int), typeof(long)); MethodInfo mSetF = R.Method(type, "Set", typeof(int), typeof(float)); MethodInfo mSetL = R.Method(type, "Set", typeof(int), typeof(long)); if (mPrefab == null || mPos == null || mGetF == null || mGetL == null || mSetF == null || mSetL == null) { return false; } try { ParameterExpression parameterExpression = Expression.Parameter(typeof(object), "o"); ParameterExpression parameterExpression2 = Expression.Parameter(typeof(int), "k"); ParameterExpression parameterExpression3 = Expression.Parameter(typeof(float), "f"); ParameterExpression parameterExpression4 = Expression.Parameter(typeof(long), "l"); Expression instance = Expression.Convert(parameterExpression, type); Prefab = Expression.Lambda<Func<object, int>>(Expression.Call(instance, mPrefab), new ParameterExpression[1] { parameterExpression }).Compile(); Position = Expression.Lambda<Func<object, Vector3>>(Expression.Call(instance, mPos), new ParameterExpression[1] { parameterExpression }).Compile(); GetFloat = Expression.Lambda<Func<object, int, float, float>>(Expression.Call(instance, mGetF, parameterExpression2, parameterExpression3), new ParameterExpression[3] { parameterExpression, parameterExpression2, parameterExpression3 }).Compile(); GetLong = Expression.Lambda<Func<object, int, long, long>>(Expression.Call(instance, mGetL, parameterExpression2, parameterExpression4), new ParameterExpression[3] { parameterExpression, parameterExpression2, parameterExpression4 }).Compile(); SetFloat = Expression.Lambda<Action<object, int, float>>(Expression.Call(instance, mSetF, parameterExpression2, parameterExpression3), new ParameterExpression[3] { parameterExpression, parameterExpression2, parameterExpression3 }).Compile(); SetLong = Expression.Lambda<Action<object, int, long>>(Expression.Call(instance, mSetL, parameterExpression2, parameterExpression4), new ParameterExpression[3] { parameterExpression, parameterExpression2, parameterExpression4 }).Compile(); } catch (Exception) { Prefab = (object x) => (int)mPrefab.Invoke(x, null); Position = (object x) => (Vector3)mPos.Invoke(x, null); GetFloat = (object x, int key, float d) => (float)mGetF.Invoke(x, new object[2] { key, d }); GetLong = (object x, int key, long d) => (long)mGetL.Invoke(x, new object[2] { key, d }); SetFloat = delegate(object x, int key, float v) { mSetF.Invoke(x, new object[2] { key, v }); }; SetLong = delegate(object x, int key, long v) { mSetL.Invoke(x, new object[2] { key, v }); }; } try { MethodInfo methodInfo = R.Method(type, "IsOwner"); ParameterExpression parameterExpression5 = Expression.Parameter(typeof(object), "o"); if (methodInfo != null) { IsOwner = Expression.Lambda<Func<object, bool>>(Expression.Call(Expression.Convert(parameterExpression5, type), methodInfo), new ParameterExpression[1] { parameterExpression5 }).Compile(); } } catch (Exception) { IsOwner = null; } s_ok = true; return true; } public static int Var(string name) { FieldInfo fieldInfo = R.Field(R.Type("ZDOVars"), name); if (fieldInfo == null) { return 0; } try { return Convert.ToInt32(fieldInfo.GetValue(null)); } catch (Exception) { return 0; } } } internal sealed class WaveDepth : Module { private static readonly int[] s_cx = new int[4] { 0, 1, 1, 0 }; private static readonly int[] s_cz = new int[4] { 1, 1, 0, 0 }; private static readonly CultureInfo Inv = CultureInfo.InvariantCulture; private ConfigEntry<float> m_interval; private ConfigEntry<float> m_weight; private ConfigEntry<bool> m_log; private bool m_resolved; private PropertyInfo pInstances; private FieldInfo fHeightmap; private FieldInfo fNormalized; private FieldInfo fOneDepth; private FieldInfo fForceDepth; private FieldInfo fHeights; private FieldInfo fWidth; private FieldInfo fBuildData; private FieldInfo fBaseHeights; private MethodInfo mSetup; private float m_nextTick; public override string Title => "Wave Depth"; protected override string Section => "05 - Wave Depth"; protected override bool DefaultOn => false; protected override string Description => "Waves are calmed by shallow ground, but the game only looks at the ground as it was generated. Make the waves follow raised and dug terrain too. Off by default."; protected override void BindSettings(ConfigFile cfg) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown m_weight = cfg.Bind<float>(Section, "Raised terrain weight", 1f, new ConfigDescription("How much of each raise counts for the waves. Effective height = natural + raise * weight. 1 = the terrain as it is now, 0.5 = half of every raise, 0 = natural height. Digging always counts in full.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[0])); m_interval = cfg.Bind<float>(Section, "Check interval", 2f, new ConfigDescription("Seconds between checks. Only four numbers per water volume are compared.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 30f), new object[0])); m_log = cfg.Bind<bool>(Section, "Log", false, "Log every water volume that is re-read."); } private bool Resolve() { if (m_resolved) { return true; } Type type = R.Type("WaterVolume"); Type type2 = R.Type("Heightmap"); if (type == null || type2 == null) { return false; } pInstances = type.GetProperty("Instances", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); fHeightmap = R.Field(type, "m_heightmap"); fNormalized = R.Field(type, "m_normalizedDepth"); fOneDepth = R.Field(type, "m_oneDepth"); fForceDepth = R.Field(type, "m_forceDepth"); mSetup = R.Method(type, "SetupMaterial"); fHeights = R.Field(type2, "m_heights"); fWidth = R.Field(type2, "m_width"); fBuildData = R.Field(type2, "m_buildData"); if (fBuildData != null) { fBaseHeights = fBuildData.FieldType.GetField("m_baseHeights", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } string text = R.Missing(pInstances, "WaterVolume.Instances", fHeightmap, "WaterVolume.m_heightmap", fNormalized, "WaterVolume.m_normalizedDepth", fOneDepth, "WaterVolume.m_oneDepth", mSetup, "WaterVolume.SetupMaterial", fHeights, "Heightmap.m_heights", fWidth, "Heightmap.m_width", fBuildData, "Heightmap.m_buildData", fBaseHeights, "m_baseHeights"); if (text != null) { Broken(text); return false; } m_resolved = true; return true; } protected override void