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 TerrainMistile v1.0.8
TerrainMistile.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; using Jotunn.Entities; using Jotunn.Managers; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Core.ObjectPool; using YamlDotNet.Core.Tokens; using YamlDotNet.Helpers; using YamlDotNet.Serialization; using YamlDotNet.Serialization.BufferedDeserialization; using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators; using YamlDotNet.Serialization.Callbacks; using YamlDotNet.Serialization.Converters; using YamlDotNet.Serialization.EventEmitters; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NodeDeserializers; using YamlDotNet.Serialization.NodeTypeResolvers; using YamlDotNet.Serialization.ObjectFactories; using YamlDotNet.Serialization.ObjectGraphTraversalStrategies; using YamlDotNet.Serialization.ObjectGraphVisitors; using YamlDotNet.Serialization.Schemas; using YamlDotNet.Serialization.TypeInspectors; using YamlDotNet.Serialization.TypeResolvers; using YamlDotNet.Serialization.Utilities; using YamlDotNet.Serialization.ValueDeserializers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("TerrainMistile")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyProduct("TerrainMistile")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")] [assembly: AssemblyFileVersion("1.0.8")] [assembly: InternalsVisibleTo("TerrainMistile.Tests")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.8.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace TerrainMistile { [BepInPlugin("sighsorry.TerrainMistile", "TerrainMistile", "1.0.8")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class TerrainMistilePlugin : BaseUnityPlugin { public enum Toggle { On = 1, Off = 0 } internal const string ModName = "TerrainMistile"; internal const string ModVersion = "1.0.8"; internal const string Author = "sighsorry"; internal const string DisplayNameToken = "$terrainmistile_creature_name"; internal const string EnforcerDisplayNameToken = "$terrainmistile_enforcer_name"; internal const string CompendiumTopicToken = "$terrainmistile_compendium_topic"; internal const string CheckCompendiumMessageToken = "$terrainmistile_message_check_compendium"; private const string ModGUID = "sighsorry.TerrainMistile"; private const string ConfigFileName = "sighsorry.TerrainMistile.cfg"; private const string SpawnRulesFileName = "TerrainMistile.yml"; private static readonly string ConfigFileFullPath = Path.Combine(Paths.ConfigPath, "sighsorry.TerrainMistile.cfg"); private static readonly string SpawnRulesFileFullPath = Path.Combine(Paths.ConfigPath, "TerrainMistile.yml"); private readonly Harmony _harmony = new Harmony("sighsorry.TerrainMistile"); public static readonly ManualLogSource TerrainMistileLogger = Logger.CreateLogSource("TerrainMistile"); private static readonly ConfigSync ConfigSync = new ConfigSync("sighsorry.TerrainMistile") { DisplayName = "TerrainMistile", CurrentVersion = "1.0.8", MinimumRequiredVersion = "1.0.8", ModRequired = true }; private FileSystemWatcher _watcher; private FileSystemWatcher _spawnRulesWatcher; private readonly object _reloadLock = new object(); private DateTime _lastConfigReloadTime; private DateTime _lastSpawnRulesReloadTime; private string? _lastConfigFileText; private string? _appliedSpawnRulesYamlText; private const long RELOAD_DELAY = 10000000L; private static ConfigEntry<Toggle> _terrainMistileEnabled = null; private static ConfigEntry<Toggle> _serverConfigLocked = null; private static CustomSyncedValue<string> SpawnRulesYaml = null; internal static bool SpawningEnabled { get { ConfigEntry<Toggle> terrainMistileEnabled = _terrainMistileEnabled; if (terrainMistileEnabled == null) { return true; } return terrainMistileEnabled.Value != Toggle.Off; } } public void Awake() { TerrainMistileLocalization.Load(TerrainMistileLogger); TerrainCompAccess.Initialize(); bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; _terrainMistileEnabled = config("1 - General", "Enable TerrainMistile", Toggle.On, "Globally enables new TerrainMistile spawns. Existing TerrainMistiles are not removed when disabled."); _serverConfigLocked = config("1 - General", "Lock Configuration", Toggle.On, "If on, the configuration is locked and can be changed by server admins only."); ConfigSync.AddLockingConfigEntry<Toggle>(_serverConfigLocked); RemoveObsoleteDisplayNameConfig(); TerrainMistileSpawnRules.Initialize(TerrainMistileLogger); TerrainMistileSpawnRules.EnsureFileExists(SpawnRulesFileFullPath); SpawnRulesYaml = new CustomSyncedValue<string>(ConfigSync, "SpawnRulesYaml", string.Empty); SpawnRulesYaml.ValueChanged += OnSyncedSpawnRulesYamlChanged; ConfigSync.SourceOfTruthChanged += OnSourceOfTruthChanged; ApplySpawnRulesYaml(File.ReadAllText(SpawnRulesFileFullPath), "local fallback"); TerrainMistilePrefab.RegisterPrefabHook(); Assembly executingAssembly = Assembly.GetExecutingAssembly(); _harmony.PatchAll(executingAssembly); TerrainMistileExternalTerrainCompat.Initialize(TerrainMistileLogger, _harmony); SetupWatcher(); if (ConfigSync.IsSourceOfTruth) { PushLocalSpawnRulesYamlToSync(); } ((BaseUnityPlugin)this).Config.Save(); _lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath); if (saveOnConfigSet) { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } private void OnDestroy() { TerrainMistilePrefab.UnregisterPrefabHook(); SpawnRulesYaml.ValueChanged -= OnSyncedSpawnRulesYamlChanged; ConfigSync.SourceOfTruthChanged -= OnSourceOfTruthChanged; SaveWithRespectToConfigSet(); _watcher?.Dispose(); _spawnRulesWatcher?.Dispose(); } private void Update() { TerrainMistileSystem.UpdateResetEffectRpcRegistration(); TerrainMistileSystem.UpdateProtectedTerrainAreaSync(); TerrainMistileExternalTerrainCompat.Update(); if (TerrainMistileExternalTerrainCompat.ConsumeBiomeMappingRefreshRequest() && _appliedSpawnRulesYamlText != null) { ApplySpawnRulesYaml(_appliedSpawnRulesYamlText, "Expand World Data biome refresh"); } TerrainMistileSystem.UpdatePersistentTerrainSpawns(); } private void SetupWatcher() { _watcher = new FileSystemWatcher(Paths.ConfigPath, "sighsorry.TerrainMistile.cfg"); _watcher.Changed += ReadConfigValues; _watcher.Created += ReadConfigValues; _watcher.Renamed += ReadConfigValues; _watcher.IncludeSubdirectories = true; _watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _watcher.EnableRaisingEvents = true; _spawnRulesWatcher = new FileSystemWatcher(Paths.ConfigPath, "TerrainMistile.yml"); _spawnRulesWatcher.Changed += ReadSpawnRulesValues; _spawnRulesWatcher.Created += ReadSpawnRulesValues; _spawnRulesWatcher.Renamed += ReadSpawnRulesValues; _spawnRulesWatcher.IncludeSubdirectories = false; _spawnRulesWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _spawnRulesWatcher.EnableRaisingEvents = true; } private void ReadConfigValues(object sender, FileSystemEventArgs e) { DateTime now = DateTime.Now; if (now.Ticks - _lastConfigReloadTime.Ticks < 10000000) { return; } lock (_reloadLock) { if (!File.Exists(ConfigFileFullPath)) { TerrainMistileLogger.LogWarning((object)"Config file does not exist. Skipping reload."); return; } try { string b = File.ReadAllText(ConfigFileFullPath); if (string.Equals(_lastConfigFileText, b, StringComparison.Ordinal)) { return; } SaveWithRespectToConfigSet(reload: true); _lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath); TerrainMistileLogger.LogInfo((object)"Configuration reload complete."); } catch (Exception ex) { TerrainMistileLogger.LogError((object)("Error reloading configuration: " + ex.Message)); } } _lastConfigReloadTime = now; } private void ReadSpawnRulesValues(object sender, FileSystemEventArgs e) { DateTime now = DateTime.Now; if (now.Ticks - _lastSpawnRulesReloadTime.Ticks < 10000000) { return; } lock (_reloadLock) { if (!File.Exists(SpawnRulesFileFullPath)) { TerrainMistileLogger.LogWarning((object)"TerrainMistile spawn rules YAML does not exist. Skipping reload."); return; } if (!ConfigSync.IsSourceOfTruth) { return; } try { PushLocalSpawnRulesYamlToSync(); TerrainMistileLogger.LogInfo((object)"TerrainMistile spawn rules YAML reload complete."); } catch (Exception ex) { TerrainMistileLogger.LogError((object)("Error reloading TerrainMistile spawn rules YAML: " + ex.Message)); } } _lastSpawnRulesReloadTime = now; } private void PushLocalSpawnRulesYamlToSync() { TerrainMistileSpawnRules.EnsureFileExists(SpawnRulesFileFullPath); string text = File.ReadAllText(SpawnRulesFileFullPath); if ((!string.Equals(_appliedSpawnRulesYamlText, text, StringComparison.Ordinal) || !string.Equals(SpawnRulesYaml.Value ?? string.Empty, text, StringComparison.Ordinal)) && ApplySpawnRulesYaml(text, "local file") && !string.Equals(SpawnRulesYaml.Value ?? string.Empty, text, StringComparison.Ordinal)) { SpawnRulesYaml.Value = text; } } private void OnSyncedSpawnRulesYamlChanged() { string text = SpawnRulesYaml.Value ?? string.Empty; if (!string.Equals(_appliedSpawnRulesYamlText, text, StringComparison.Ordinal)) { ApplySpawnRulesYaml(text, ConfigSync.IsSourceOfTruth ? "local sync" : "server sync"); } } private void OnSourceOfTruthChanged(bool isSourceOfTruth) { if (isSourceOfTruth) { PushLocalSpawnRulesYamlToSync(); return; } string text = SpawnRulesYaml.Value ?? string.Empty; if (!string.Equals(_appliedSpawnRulesYamlText, text, StringComparison.Ordinal)) { ApplySpawnRulesYaml(text, "server sync"); } } private bool ApplySpawnRulesYaml(string yaml, string source) { TerrainMistileExpandWorldDataBiomeCompat.InvalidateFileMapping(); if (!TerrainMistileSpawnRules.LoadYamlText(yaml, source)) { return false; } _appliedSpawnRulesYamlText = yaml; TerrainMistileSystem.InvalidateSpawnRuleState(); TerrainMistilePrefab.RefreshRegisteredPrefabVisuals(); return true; } private static string? ReadFileTextIfExists(string path) { if (!File.Exists(path)) { return null; } return File.ReadAllText(path); } private void SaveWithRespectToConfigSet(bool reload = false) { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; if (reload) { ((BaseUnityPlugin)this).Config.Reload(); } ((BaseUnityPlugin)this).Config.Save(); if (saveOnConfigSet) { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } private void RemoveObsoleteDisplayNameConfig() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown ConfigDefinition val = new ConfigDefinition("2 - Display", "Display Name"); ((BaseUnityPlugin)this).Config.Bind<string>(val, string.Empty, (ConfigDescription)null); ((BaseUnityPlugin)this).Config.Remove(val); } private ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, description.Tags); ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val); ConfigSync.AddConfigEntry<T>(val2).SynchronizedConfig = synchronizedSetting; return val2; } private ConfigEntry<T> config<T>(string group, string name, T value, string description, bool synchronizedSetting = true) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting); } } internal static class TerrainCompAccess { private delegate void SaveTerrainComp(TerrainComp terrainComp); private sealed class Bindings { internal readonly FieldInfo Instances = RequireField("s_instances", typeof(List<TerrainComp>), isStatic: true); internal readonly FieldInfo Heightmap = RequireField("m_hmap", typeof(Heightmap)); internal readonly FieldInfo Operations = RequireField("m_operations", typeof(int)); internal readonly FieldInfo ModifiedHeight = RequireField("m_modifiedHeight", typeof(bool[])); internal readonly FieldInfo LevelDelta = RequireField("m_levelDelta", typeof(float[])); internal readonly FieldInfo SmoothDelta = RequireField("m_smoothDelta", typeof(float[])); internal readonly FieldInfo ModifiedPaint = RequireField("m_modifiedPaint", typeof(bool[])); internal readonly FieldInfo PaintMask = RequireField("m_paintMask", typeof(Color[])); internal readonly FieldInfo LastOperationPoint = RequireField("m_lastOpPoint", typeof(Vector3)); internal readonly FieldInfo LastOperationRadius = RequireField("m_lastOpRadius", typeof(float)); internal readonly MethodInfo Save = RequireMethod("Save"); } internal readonly struct ScanData { internal readonly Heightmap Heightmap; internal readonly int VertexWidth; internal readonly int Operations; internal readonly bool[] ModifiedHeight; internal readonly float[] LevelDelta; internal readonly float[] SmoothDelta; internal ScanData(Heightmap heightmap, int vertexWidth, int operations, bool[] modifiedHeight, float[] levelDelta, float[] smoothDelta) { Heightmap = heightmap; VertexWidth = vertexWidth; Operations = operations; ModifiedHeight = modifiedHeight; LevelDelta = levelDelta; SmoothDelta = smoothDelta; } } internal readonly struct ResetData { internal readonly ScanData Scan; internal readonly bool[] ModifiedPaint; internal readonly Color[] PaintMask; internal ResetData(ScanData scan, bool[] modifiedPaint, Color[] paintMask) { Scan = scan; ModifiedPaint = modifiedPaint; PaintMask = paintMask; } } private static FieldRef<List<TerrainComp>> _instances; private static FieldRef<TerrainComp, Heightmap> _heightmap; private static FieldRef<TerrainComp, int> _operations; private static FieldRef<TerrainComp, bool[]> _modifiedHeight; private static FieldRef<TerrainComp, float[]> _levelDelta; private static FieldRef<TerrainComp, float[]> _smoothDelta; private static FieldRef<TerrainComp, bool[]> _modifiedPaint; private static FieldRef<TerrainComp, Color[]> _paintMask; private static FieldRef<TerrainComp, Vector3> _lastOperationPoint; private static FieldRef<TerrainComp, float> _lastOperationRadius; private static SaveTerrainComp _save; private static bool _initialized; internal static List<TerrainComp> Instances => _instances.Invoke(); internal static void Initialize() { if (!_initialized) { Bindings bindings = ResolveBindings(); _instances = AccessTools.StaticFieldRefAccess<List<TerrainComp>>(bindings.Instances); _heightmap = AccessTools.FieldRefAccess<TerrainComp, Heightmap>(bindings.Heightmap); _operations = AccessTools.FieldRefAccess<TerrainComp, int>(bindings.Operations); _modifiedHeight = AccessTools.FieldRefAccess<TerrainComp, bool[]>(bindings.ModifiedHeight); _levelDelta = AccessTools.FieldRefAccess<TerrainComp, float[]>(bindings.LevelDelta); _smoothDelta = AccessTools.FieldRefAccess<TerrainComp, float[]>(bindings.SmoothDelta); _modifiedPaint = AccessTools.FieldRefAccess<TerrainComp, bool[]>(bindings.ModifiedPaint); _paintMask = AccessTools.FieldRefAccess<TerrainComp, Color[]>(bindings.PaintMask); _lastOperationPoint = AccessTools.FieldRefAccess<TerrainComp, Vector3>(bindings.LastOperationPoint); _lastOperationRadius = AccessTools.FieldRefAccess<TerrainComp, float>(bindings.LastOperationRadius); _save = AccessTools.MethodDelegate<SaveTerrainComp>(bindings.Save, (object)null, false); _initialized = true; } } internal static bool TryCaptureScanData(TerrainComp terrainComp, out ScanData data) { Heightmap val = _heightmap.Invoke(terrainComp); bool[] array = _modifiedHeight.Invoke(terrainComp); float[] array2 = _levelDelta.Invoke(terrainComp); float[] array3 = _smoothDelta.Invoke(terrainComp); int num = (Object.op_Implicit((Object)(object)val) ? (val.m_width + 1) : 0); long num2 = (long)num * (long)num; if (!Object.op_Implicit((Object)(object)val) || num <= 0 || num2 > int.MaxValue || array == null || array2 == null || array3 == null || array.Length < num2 || array2.Length < num2 || array3.Length < num2) { data = default(ScanData); return false; } data = new ScanData(val, num, _operations.Invoke(terrainComp), array, array2, array3); return true; } internal static bool TryCaptureResetData(TerrainComp terrainComp, bool includePaint, out ResetData data) { if (!TryCaptureScanData(terrainComp, out var data2)) { data = default(ResetData); return false; } if (!includePaint) { data = new ResetData(data2, Array.Empty<bool>(), Array.Empty<Color>()); return true; } bool[] array = _modifiedPaint.Invoke(terrainComp); Color[] array2 = _paintMask.Invoke(terrainComp); int num = data2.VertexWidth * data2.VertexWidth; if (array == null || array2 == null || array.Length < num || array2.Length < num) { data = default(ResetData); return false; } data = new ResetData(data2, array, array2); return true; } internal static void CommitReset(TerrainComp terrainComp, Vector3 center, float radius) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) _operations.Invoke(terrainComp)++; _lastOperationPoint.Invoke(terrainComp) = center; _lastOperationRadius.Invoke(terrainComp) = radius; _save(terrainComp); } private static Bindings ResolveBindings() { return new Bindings(); } private static FieldInfo RequireField(string name, Type fieldType, bool isStatic = false) { FieldInfo fieldInfo = typeof(TerrainComp).GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingFieldException(typeof(TerrainComp).FullName, name); if (fieldInfo.FieldType != fieldType || fieldInfo.IsStatic != isStatic) { throw new InvalidOperationException(typeof(TerrainComp).FullName + "." + name + " no longer matches the expected field contract."); } return fieldInfo; } private static MethodInfo RequireMethod(string name) { MethodInfo methodInfo = typeof(TerrainComp).GetMethod(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) ?? throw new MissingMethodException(typeof(TerrainComp).FullName, name); if (methodInfo.IsStatic || methodInfo.ReturnType != typeof(void) || methodInfo.GetParameters().Length != 0) { throw new InvalidOperationException(typeof(TerrainComp).FullName + "." + name + " no longer matches the expected method contract."); } return methodInfo; } } public class TerrainMistileBehaviour : MonoBehaviour { internal const string SelfDestructZdoKey = "TerrainMistileSelfDestruct"; internal const string TerrainSourceZdoKey = "TerrainMistileSource"; internal const string TerrainSourceSetZdoKey = "TerrainMistileSourceSet"; internal const string ResetRadiusZdoKey = "TerrainMistileResetRadius"; internal const string VisualColorZdoKey = "TerrainMistileVisualColor"; internal const string VisualColorSetZdoKey = "TerrainMistileVisualColorSet"; private const float TerrainImpactDistance = 1.25f; private const float TerrainImpactHeight = 0.85f; private const float TerrainTargetGroundOffset = 0.15f; private const float VisualSyncRetryInterval = 0.25f; private const float EnforcerTargetSearchInterval = 1f; private const float EnforcerTargetSearchTimeout = 10f; private Character _character; private MonsterAI _monsterAI; private ZNetView _nview; [SerializeField] private bool _enforcer; private Vector3 _terrainTarget; private float _resetRadius = 8f; private bool _hasTerrainTarget; private bool _selfDestructTriggered; private bool _terrainResetDone; private bool _terrainResetInProgress; private bool _visualColorApplied; private float _nextVisualSyncTime; private bool _enforcerTargetSearchInitialized; private float _nextEnforcerTargetSearchTime; private float _enforcerTargetSearchDeadline; private Vector3 _enforcerSpawnPoint; private readonly List<Material> _ownedVisualMaterials = new List<Material>(); private void Awake() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) _enforcerSpawnPoint = ((Component)this).transform.position; _character = ((Component)this).GetComponent<Character>(); _monsterAI = ((Component)this).GetComponent<MonsterAI>(); _nview = ((Component)this).GetComponent<ZNetView>(); TryApplySyncedVisualColor(); ApplyIdentity(); ConfigureTerrainSeeker(); TerrainMistilePrefab.MakeCollidersNonBlocking(((Component)this).gameObject); TerrainMistileSystem.RegisterActiveTerrainMistile(this); if (Object.op_Implicit((Object)(object)_character)) { Character character = _character; character.m_onDeath = (Action)Delegate.Combine(character.m_onDeath, new Action(OnDeath)); } } private void OnDestroy() { if (Object.op_Implicit((Object)(object)_character)) { Character character = _character; character.m_onDeath = (Action)Delegate.Remove(character.m_onDeath, new Action(OnDeath)); } TerrainMistilePrefab.ReleaseVisualMaterials(_ownedVisualMaterials); ReleaseCurrentTerrainTarget(); TerrainMistileSystem.UnregisterActiveTerrainMistile(this); } private void FixedUpdate() { TryApplySyncedVisualColor(); if (Object.op_Implicit((Object)(object)_nview) && _nview.IsValid() && _nview.IsOwner() && Object.op_Implicit((Object)(object)_character) && !_character.IsDead() && !_terrainResetDone && (TryLoadTerrainTarget() || TryAcquireEnforcerTerrainTarget())) { MoveTowardTerrainTarget(); } } internal void Initialize(Vector3 terrainOperationPoint, float resetRadius, float health) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) ApplyIdentity(); ConfigureTerrainSeeker(); ApplyHealth(health); SetTerrainTarget(terrainOperationPoint, resetRadius); } internal void ConfigureEnforcer(bool enforcer) { _enforcer = enforcer; } internal void MarkSelfDestruct() { _selfDestructTriggered = true; if (Object.op_Implicit((Object)(object)_nview) && _nview.IsValid() && _nview.IsOwner()) { _nview.GetZDO().Set("TerrainMistileSelfDestruct", true); } } private void ApplyIdentity() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)_character)) { _character.m_name = (_enforcer ? "$terrainmistile_enforcer_name" : "$terrainmistile_creature_name"); _character.m_faction = (Faction)8; _character.m_aiSkipTarget = true; _character.m_flying = true; } } private void ApplyHealth(float health) { if (Object.op_Implicit((Object)(object)_character) && Object.op_Implicit((Object)(object)_nview) && _nview.IsValid() && _nview.IsOwner()) { health = Mathf.Max(1f, health); _character.SetMaxHealth(health); _character.SetHealth(health); } } private void ApplyVisualColor(Color color, bool syncToZdo) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) TerrainMistilePrefab.ApplyVisuals(((Component)this).gameObject, color, _ownedVisualMaterials); _visualColorApplied = true; if (syncToZdo && Object.op_Implicit((Object)(object)_nview) && _nview.IsValid() && _nview.IsOwner()) { ZDO zDO = _nview.GetZDO(); if (zDO != null) { zDO.Set("TerrainMistileVisualColor", new Vector3(color.r, color.g, color.b)); zDO.Set("TerrainMistileVisualColorSet", true); } } } private void ApplyVisualColorForTarget(Vector3 target) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) ApplyVisualColor(TerrainMistileSpawnRules.GetRule(TerrainMistileSpawnRules.GetBiomeKey(target)).VisualColor, syncToZdo: true); } private void TryApplySyncedVisualColor() { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) if (_visualColorApplied || Time.time < _nextVisualSyncTime) { return; } _nextVisualSyncTime = Time.time + 0.25f; if (Object.op_Implicit((Object)(object)_nview) && _nview.IsValid()) { ZDO zDO = _nview.GetZDO(); if (zDO != null && zDO.GetBool("TerrainMistileVisualColorSet", false)) { Vector3 vec = zDO.GetVec3("TerrainMistileVisualColor", Vector3.zero); ApplyVisualColor(new Color(vec.x, vec.y, vec.z, 1f), syncToZdo: false); } } } private void ConfigureTerrainSeeker() { if (Object.op_Implicit((Object)(object)_monsterAI)) { _monsterAI.m_enableHuntPlayer = false; _monsterAI.m_attackPlayerObjects = false; ((BaseAI)_monsterAI).m_aggravatable = false; ZNetView component = ((Component)_monsterAI).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component) && component.IsValid()) { ((BaseAI)_monsterAI).SetHuntPlayer(false); } ((Behaviour)_monsterAI).enabled = false; } } private void SetTerrainTarget(Vector3 terrainOperationPoint, float resetRadius) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) ReleaseCurrentTerrainTarget(); _terrainTarget = terrainOperationPoint; _resetRadius = NormalizeResetRadius(resetRadius); _hasTerrainTarget = true; TerrainMistileSystem.ReserveTerrainTarget(_terrainTarget, _resetRadius); ApplyVisualColorForTarget(_terrainTarget); if (Object.op_Implicit((Object)(object)_nview) && _nview.IsValid() && _nview.IsOwner()) { ZDO zDO = _nview.GetZDO(); zDO.Set("TerrainMistileSource", terrainOperationPoint); zDO.Set("TerrainMistileResetRadius", _resetRadius); zDO.Set("TerrainMistileSourceSet", true); } } private void ReleaseCurrentTerrainTarget() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (_hasTerrainTarget) { TerrainMistileSystem.ReleaseTerrainTarget(_terrainTarget); _hasTerrainTarget = false; } } private bool TryLoadTerrainTarget() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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) if (!TryGetTerrainTarget(out var terrainTarget, out var resetRadius)) { return false; } if (!_hasTerrainTarget) { _terrainTarget = terrainTarget; _resetRadius = resetRadius; _hasTerrainTarget = true; TerrainMistileSystem.ReserveTerrainTarget(_terrainTarget, _resetRadius); } return true; } private bool TryAcquireEnforcerTerrainTarget() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) if (!_enforcer) { return false; } if (!_enforcerTargetSearchInitialized) { _enforcerTargetSearchInitialized = true; _nextEnforcerTargetSearchTime = Time.time; _enforcerTargetSearchDeadline = Time.time + 10f; } if (Time.time < _nextEnforcerTargetSearchTime) { return false; } _nextEnforcerTargetSearchTime = Time.time + 1f; if (TerrainMistileSystem.TryFindEnforcerTerrainTarget(_enforcerSpawnPoint, out var modifiedPoint)) { TerrainMistileBiomeSpawnRule rule = TerrainMistileSpawnRules.GetRule(TerrainMistileSpawnRules.GetBiomeKey(modifiedPoint)); Initialize(modifiedPoint, rule.ResetRadius, rule.Health); return true; } if (Time.time >= _enforcerTargetSearchDeadline) { DestroyWithoutTerrainReset(); } return false; } internal bool TryGetTerrainTarget(out Vector3 terrainTarget) { float resetRadius; return TryGetTerrainTarget(out terrainTarget, out resetRadius); } internal bool TryGetTerrainTarget(out Vector3 terrainTarget, out float resetRadius) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) if (_hasTerrainTarget) { terrainTarget = _terrainTarget; resetRadius = _resetRadius; return true; } if (!Object.op_Implicit((Object)(object)_nview) || !_nview.IsValid()) { terrainTarget = default(Vector3); resetRadius = _resetRadius; return false; } ZDO zDO = _nview.GetZDO(); if (zDO == null || !zDO.GetBool("TerrainMistileSourceSet", false)) { terrainTarget = default(Vector3); resetRadius = _resetRadius; return false; } terrainTarget = zDO.GetVec3("TerrainMistileSource", ((Component)this).transform.position); if (!TerrainMistileSystem.IsFinite(terrainTarget)) { terrainTarget = default(Vector3); resetRadius = _resetRadius; return false; } resetRadius = NormalizeResetRadius(zDO.GetFloat("TerrainMistileResetRadius", 0f)); return true; } private float NormalizeResetRadius(float resetRadius) { if (float.IsNaN(resetRadius) || float.IsInfinity(resetRadius)) { return _resetRadius; } return Mathf.Clamp(resetRadius, 0.1f, 64f); } private void MoveTowardTerrainTarget() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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_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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)this).transform.position; Vector3 terrainTarget = _terrainTarget; if (TerrainMistileSystem.TryGetGroundHeight(terrainTarget, out var height)) { terrainTarget.y = height + 0.15f; } Vector3 val = terrainTarget - position; Vector2 val2 = new Vector2(val.x, val.z); bool num = ((Vector2)(ref val2)).magnitude <= 1.25f || ((Vector3)(ref val)).sqrMagnitude <= 1.5625f; float height2; bool flag = TerrainMistileSystem.TryGetGroundHeight(position, out height2) && position.y <= height2 + 0.85f; if (num && flag) { DetonateOnTerrain(); return; } if (((Vector3)(ref val)).sqrMagnitude <= 0.01f) { DetonateOnTerrain(); return; } Vector3 normalized = ((Vector3)(ref val)).normalized; _character.SetRun(true); _character.SetMoveDir(normalized); Vector3 val3 = default(Vector3); ((Vector3)(ref val3))..ctor(normalized.x, 0f, normalized.z); if (((Vector3)(ref val3)).sqrMagnitude > 0.001f) { _character.SetLookDir(((Vector3)(ref val3)).normalized, 0f); } } private void DetonateOnTerrain() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (_terrainResetDone || !Object.op_Implicit((Object)(object)_character) || _character.IsDead() || TryRetargetOrDespawnFromEmptyImpact()) { return; } MarkSelfDestruct(); if (TryResetTerrain()) { HitData val = new HitData(); val.m_point = ((Component)this).transform.position; val.m_damage.m_damage = 9999999f; val.m_hitType = (HitType)14; _character.ApplyDamage(val, false, true, (DamageModifier)0); if (!_character.IsDead() && Object.op_Implicit((Object)(object)_nview) && _nview.IsValid() && _nview.IsOwner() && Object.op_Implicit((Object)(object)ZNetScene.instance)) { ZNetScene.instance.Destroy(((Component)this).gameObject); } } } private bool TryRetargetOrDespawnFromEmptyImpact() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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) TryLoadTerrainTarget(); if (!_hasTerrainTarget) { return false; } if (TerrainMistileSystem.HasModifiedTerrainAround(_terrainTarget, _resetRadius)) { return false; } ReleaseCurrentTerrainTarget(); if (_enforcer ? TerrainMistileSystem.TryFindEnforcerTerrainTarget(_enforcerSpawnPoint, out var modifiedPoint) : TerrainMistileSystem.TryFindReplacementTerrainTarget(((Component)this).transform.position, out modifiedPoint)) { SetTerrainTarget(modifiedPoint, TerrainMistileSystem.GetResetRadiusForPoint(modifiedPoint)); return true; } DestroyWithoutTerrainReset(); return true; } private void DestroyWithoutTerrainReset() { _terrainResetDone = true; ReleaseCurrentTerrainTarget(); if (Object.op_Implicit((Object)(object)_nview) && _nview.IsValid() && _nview.IsOwner() && Object.op_Implicit((Object)(object)ZNetScene.instance)) { ZNetScene.instance.Destroy(((Component)this).gameObject); } else { Object.Destroy((Object)(object)((Component)this).gameObject); } } private void OnDeath() { TryResetTerrain(); } internal bool TryResetTerrain() { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)_nview) && _nview.IsValid()) { if (!_nview.IsOwner()) { return false; } _selfDestructTriggered |= _nview.GetZDO().GetBool("TerrainMistileSelfDestruct", false); } if (_terrainResetDone) { return true; } if (_terrainResetInProgress || !_selfDestructTriggered) { return false; } _terrainResetInProgress = true; try { TryLoadTerrainTarget(); if (!TerrainMistileSystem.ResetTerrainAround(_hasTerrainTarget ? _terrainTarget : ((Component)this).transform.position, _resetRadius, resetPaint: true)) { return false; } _terrainResetDone = true; ReleaseCurrentTerrainTarget(); return true; } catch (Exception ex) { TerrainMistilePlugin.TerrainMistileLogger.LogError((object)("TerrainMistile terrain reset failed: " + ex.Message)); return false; } finally { _terrainResetInProgress = false; } } } [HarmonyPatch(typeof(TextsDialog), "UpdateTextsList")] internal static class TerrainMistileCompendium { private readonly struct VanillaBiomeEntry { internal readonly Biome Biome; internal readonly string EnglishName; internal VanillaBiomeEntry(Biome biome, string englishName) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) Biome = biome; EnglishName = englishName; } } private readonly struct BiomeDisplayEntry { internal readonly int BiomeKey; internal readonly string DisplayName; internal BiomeDisplayEntry(int biomeKey, string displayName) { BiomeKey = biomeKey; DisplayName = displayName; } } private readonly struct PrefabDisplayEntry { internal readonly string PrefabName; internal readonly string DisplayName; internal PrefabDisplayEntry(string prefabName, string displayName) { PrefabName = prefabName; DisplayName = displayName; } } private const string PageTopic = "$terrainmistile_compendium_topic"; private const string ExplanationToken = "$terrainmistile_compendium_explanation"; private const string ProtectionHeadingToken = "$terrainmistile_compendium_protection_heading"; private const string RequirementToken = "$terrainmistile_compendium_requirement"; private const string MistileDisabledToken = "$terrainmistile_compendium_mistile_disabled"; private const string ProtectionDisabledToken = "$terrainmistile_compendium_protection_disabled"; private const string OtherBiomesToken = "$terrainmistile_compendium_other_biomes"; private const string PiecesHeadingToken = "$terrainmistile_compendium_pieces_heading"; private const string NoPiecesToken = "$terrainmistile_compendium_no_pieces"; private static readonly VanillaBiomeEntry[] VanillaBiomes = new VanillaBiomeEntry[9] { new VanillaBiomeEntry((Biome)1, "Meadows"), new VanillaBiomeEntry((Biome)8, "Black Forest"), new VanillaBiomeEntry((Biome)2, "Swamp"), new VanillaBiomeEntry((Biome)4, "Mountain"), new VanillaBiomeEntry((Biome)16, "Plains"), new VanillaBiomeEntry((Biome)512, "Mistlands"), new VanillaBiomeEntry((Biome)32, "Ashlands"), new VanillaBiomeEntry((Biome)64, "Deep North"), new VanillaBiomeEntry((Biome)256, "Ocean") }; private static void Postfix(List<TextInfo> ___m_texts) { AddPage(___m_texts); } private static void AddPage(List<TextInfo> texts) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown if (texts != null) { string localizedTopic = LocalizeOrFallback("$terrainmistile_compendium_topic", "TerrainMistile"); texts.RemoveAll((TextInfo text) => string.Equals(text?.m_topic, "$terrainmistile_compendium_topic", StringComparison.Ordinal) || string.Equals(text?.m_topic, localizedTopic, StringComparison.Ordinal)); texts.Add(new TextInfo(localizedTopic, BuildPageText(localizedTopic))); texts.Sort((TextInfo left, TextInfo right) => string.Compare(left?.m_topic, right?.m_topic, StringComparison.OrdinalIgnoreCase)); } } private static string BuildPageText(string localizedTopic) { StringBuilder stringBuilder = new StringBuilder(4096); stringBuilder.Append(LocalizeOrFallback("$terrainmistile_compendium_explanation", "Protection counts different recognized settlement facility types around each altered terrain point. Multiple copies of the same facility count as one type. " + localizedTopic + " spawning is blocked on protected terrain.", localizedTopic)); stringBuilder.Append("\n\n"); AppendBiomeRules(stringBuilder, localizedTopic); stringBuilder.Append("\n\n"); AppendPlayerBasePrefabs(stringBuilder); return stringBuilder.ToString().TrimEnd(Array.Empty<char>()); } private static void AppendBiomeRules(StringBuilder builder, string localizedTopic) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected I4, but got Unknown builder.Append("<color=#FFD27A><b>").Append(LocalizeOrFallback("$terrainmistile_compendium_protection_heading", "Settlement Protection by Biome")).Append("</b></color>\n\n"); HashSet<int> hashSet = new HashSet<int>(); VanillaBiomeEntry[] vanillaBiomes = VanillaBiomes; for (int i = 0; i < vanillaBiomes.Length; i++) { VanillaBiomeEntry biome = vanillaBiomes[i]; int num = (int)biome.Biome; hashSet.Add(num); AppendBiomeRule(builder, GetLocalizedVanillaBiomeName(biome), TerrainMistileSpawnRules.GetRule(num), localizedTopic); } List<BiomeDisplayEntry> list = new List<BiomeDisplayEntry>(); foreach (int item in TerrainMistileSpawnRules.GetConfiguredBiomeKeysSnapshot()) { if (item != 0 && hashSet.Add(item)) { list.Add(new BiomeDisplayEntry(item, GetLocalizedCustomBiomeName(item))); } } list.Sort(delegate(BiomeDisplayEntry left, BiomeDisplayEntry right) { int num2 = string.Compare(left.DisplayName, right.DisplayName, StringComparison.OrdinalIgnoreCase); if (num2 == 0) { int biomeKey = left.BiomeKey; return biomeKey.CompareTo(right.BiomeKey); } return num2; }); foreach (BiomeDisplayEntry item2 in list) { AppendBiomeRule(builder, item2.DisplayName, TerrainMistileSpawnRules.GetRule(item2.BiomeKey), localizedTopic); } AppendBiomeRule(builder, LocalizeOrFallback("$terrainmistile_compendium_other_biomes", "Other biomes (defaults)"), TerrainMistileSpawnRules.DefaultRule, localizedTopic); } private static void AppendBiomeRule(StringBuilder builder, string biomeName, TerrainMistileBiomeSpawnRule rule, string localizedTopic) { builder.Append("<color=orange><b>").Append(biomeName).Append("</b></color>: "); if (!rule.Enabled) { builder.Append(LocalizeOrFallback("$terrainmistile_compendium_mistile_disabled", localizedTopic + " spawning disabled", localizedTopic)); } else if (rule.PlayerBaseValue <= 0 || rule.BaseCheckRadius <= 0f) { builder.Append(LocalizeOrFallback("$terrainmistile_compendium_protection_disabled", "Settlement protection disabled")); } else { string text = rule.PlayerBaseValue.ToString(CultureInfo.InvariantCulture); string text2 = rule.BaseCheckRadius.ToString("0.##", CultureInfo.InvariantCulture); builder.Append(LocalizeOrFallback("$terrainmistile_compendium_requirement", "Required facility types: " + text + " · Check radius: " + text2 + " m", text, text2)); } builder.Append('\n'); } private static void AppendPlayerBasePrefabs(StringBuilder builder) { List<PrefabDisplayEntry> list = new List<PrefabDisplayEntry>(); foreach (string item in TerrainMistileSpawnRules.GetPlayerBasePrefabNamesSnapshot()) { list.Add(new PrefabDisplayEntry(item, GetPrefabDisplayName(item))); } list.Sort(delegate(PrefabDisplayEntry left, PrefabDisplayEntry right) { int num = string.Compare(left.DisplayName, right.DisplayName, StringComparison.OrdinalIgnoreCase); return (num == 0) ? string.Compare(left.PrefabName, right.PrefabName, StringComparison.OrdinalIgnoreCase) : num; }); Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); foreach (PrefabDisplayEntry item2 in list) { dictionary.TryGetValue(item2.DisplayName, out var value); dictionary[item2.DisplayName] = value + 1; } string text = list.Count.ToString(CultureInfo.InvariantCulture); builder.Append("<color=#FFD27A><b>").Append(LocalizeOrFallback("$terrainmistile_compendium_pieces_heading", "Recognized Settlement Facilities (" + text + ")", text)).Append("</b></color>\n\n"); if (list.Count == 0) { builder.Append(LocalizeOrFallback("$terrainmistile_compendium_no_pieces", "No settlement facilities configured.")).Append('\n'); return; } foreach (PrefabDisplayEntry item3 in list) { builder.Append("- ").Append(item3.DisplayName); if (dictionary[item3.DisplayName] > 1 && !string.Equals(item3.DisplayName, item3.PrefabName, StringComparison.OrdinalIgnoreCase)) { builder.Append(" <color=#999999>[").Append(item3.PrefabName).Append("]</color>"); } builder.Append('\n'); } } private static string GetPrefabDisplayName(string prefabName) { GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab(prefabName) : null); if (!Object.op_Implicit((Object)(object)val)) { val = PrefabManager.Instance.GetPrefab(prefabName); } Piece val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<Piece>() : null); if (!Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val)) { val2 = val.GetComponentInChildren<Piece>(true); } if (!Object.op_Implicit((Object)(object)val2) || string.IsNullOrWhiteSpace(val2.m_name)) { return prefabName; } return LocalizeOrFallback(val2.m_name, prefabName); } private static string GetLocalizedVanillaBiomeName(VanillaBiomeEntry biome) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) return LocalizeOrFallback("$biome_" + ((object)biome.Biome/*cast due to .constrained prefix*/).ToString().ToLowerInvariant(), biome.EnglishName); } private static string GetLocalizedCustomBiomeName(int biomeKey) { string biomeName = TerrainMistileSpawnRules.GetBiomeName(biomeKey); if (string.IsNullOrWhiteSpace(biomeName)) { return biomeKey.ToString(CultureInfo.InvariantCulture); } string fallback = ((biomeName[0] == '$') ? biomeName.Substring(1) : biomeName); return LocalizeOrFallback(biomeName, fallback); } private static string LocalizeOrFallback(string value, string fallback, params string[] words) { if (string.IsNullOrWhiteSpace(value) || Localization.instance == null) { return fallback; } string text = Localization.instance.Localize(value, words).Trim(); if (text.Length == 0) { return fallback; } if (value[0] == '$') { string b = "[" + value.Substring(1) + "]"; if (string.Equals(text, b, StringComparison.Ordinal) || text.IndexOf("MISSING KEY", StringComparison.OrdinalIgnoreCase) >= 0) { return fallback; } } return text; } } public static class TerrainMistileCompat { public static void RegisterIgnoredTerrainArea(Vector3 center, float radius, string source) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) TerrainMistileSystem.RegisterExternalTerrainIgnoreArea(center, radius, source); } } internal static class TerrainMistileExternalTerrainCompat { private const string ExpandWorldDataLocationTypeName = "ExpandWorldData.LocationObjectDataAndSwap"; private const string ExpandWorldDataLocationYamlTypeName = "ExpandWorldData.LocationYaml"; private const string ExpandWorldDataLocationExtraTypeName = "ExpandWorldData.LocationExtra"; private const string ExpandWorldDataBlueprintManagerTypeName = "ExpandWorldData.BlueprintManager"; private const string ExpandWorldDataNoBuildManagerTypeName = "ExpandWorldData.NoBuildManager"; private const string ExpandWorldDataBiomeManagerTypeName = "ExpandWorldData.BiomeManager"; private const string BlueprintProtectedSourcePrefix = "Expand World Data blueprint terrain"; private const float PatchRetryInterval = 2f; private const float MissingDependencyRetryInterval = 10f; private const int MaxPatchAttempts = 15; private static readonly Dictionary<string, Type> LoadedTypes = new Dictionary<string, Type>(StringComparer.Ordinal); private static readonly Dictionary<Type, Dictionary<string, FieldInfo?>> FieldsByType = new Dictionary<Type, Dictionary<string, FieldInfo>>(); private static ManualLogSource? _logger; private static Harmony? _harmony; private static bool _terrainPatched; private static bool _protectionSyncPatched; private static bool _biomeMappingRefreshPatched; private static bool _biomeNamesFromFilePatched; private static bool _biomeSetNamesPatched; private static bool _biomeLoadPatched; private static bool _biomeMappingRefreshRequested; private static bool _expandWorldDataDetected; private static bool _blueprintReflectionResolved; private static int _patchAttempts; private static float _nextPatchAttemptTime; private static MethodInfo? _tryGetLocationYamlMethod; private static MethodInfo? _isBlueprintPrefabMethod; public static void Initialize(ManualLogSource logger, Harmony harmony) { _logger = logger; _harmony = harmony; bool flag = TryPatch(); _nextPatchAttemptTime = Time.time + (flag ? 2f : 10f); } public static void Update() { if ((!_terrainPatched || !_protectionSyncPatched || !_biomeMappingRefreshPatched) && _patchAttempts < 15 && !(Time.time < _nextPatchAttemptTime)) { bool flag = TryPatch(); _nextPatchAttemptTime = Time.time + (flag ? 2f : 10f); } } internal static bool ConsumeBiomeMappingRefreshRequest() { if (!_biomeMappingRefreshRequested) { return false; } _biomeMappingRefreshRequested = false; return true; } private static bool TryPatch() { if (_harmony == null || _patchAttempts >= 15) { return false; } if (FindLoadedType("ExpandWorldData.BiomeManager") == null) { return false; } if (!_expandWorldDataDetected) { _expandWorldDataDetected = true; _biomeMappingRefreshRequested = true; } _patchAttempts++; TryPatchTerrainHandler(); TryPatchBlueprintProtectionSync(); TryPatchBiomeMappingRefresh(); return true; } private static void TryPatchTerrainHandler() { //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Expected O, but got Unknown if (_terrainPatched || _harmony == null) { return; } Type type = FindLoadedType("ExpandWorldData.LocationObjectDataAndSwap"); Type type2 = FindLoadedType("ExpandWorldData.LocationYaml"); if (type == null || type2 == null) { return; } MethodInfo methodInfo = AccessTools.Method(type, "HandleTerrain", new Type[4] { typeof(Vector3), typeof(float), typeof(bool), type2 }, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(TerrainMistileExternalTerrainCompat), "HandleTerrainPrefix", (Type[])null, (Type[])null); if (!(methodInfo == null) && !(methodInfo2 == null)) { _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _terrainPatched = true; ManualLogSource? logger = _logger; if (logger != null) { logger.LogInfo((object)"Expand World Data terrain compat initialized."); } } } private static void TryPatchBlueprintProtectionSync() { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown if (_protectionSyncPatched || _harmony == null) { return; } Type type = FindLoadedType("ExpandWorldData.NoBuildManager"); if (type == null) { return; } MethodInfo methodInfo = AccessTools.Method(type, "UpdateData", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(TerrainMistileExternalTerrainCompat), "BlueprintProtectionSyncPostfix", (Type[])null, (Type[])null); if (!(methodInfo == null) && !(methodInfo2 == null)) { _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _protectionSyncPatched = true; if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && Object.op_Implicit((Object)(object)ZoneSystem.instance) && ZoneSystem.instance.m_locationInstances.Count > 0) { SyncBlueprintProtectedAreas(); } ManualLogSource? logger = _logger; if (logger != null) { logger.LogInfo((object)"Expand World Data blueprint terrain protection initialized."); } } } private static void TryPatchBiomeMappingRefresh() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown if (_biomeMappingRefreshPatched || _harmony == null) { return; } Type type = FindLoadedType("ExpandWorldData.BiomeManager"); if (type == null) { return; } MethodInfo methodInfo = AccessTools.Method(typeof(TerrainMistileExternalTerrainCompat), "BiomeMappingChangedPostfix", (Type[])null, (Type[])null); if (methodInfo == null) { return; } HarmonyMethod postfix = new HarmonyMethod(methodInfo); List<string> list = new List<string>(); TryPatchBiomeMappingRefreshMethod(type, "NamesFromFile", Type.EmptyTypes, postfix, ref _biomeNamesFromFilePatched, list); TryPatchBiomeMappingRefreshMethod(type, "SetNames", null, postfix, ref _biomeSetNamesPatched, list); TryPatchBiomeMappingRefreshMethod(type, "Load", new Type[1] { typeof(string) }, postfix, ref _biomeLoadPatched, list); _biomeMappingRefreshPatched = _biomeNamesFromFilePatched && _biomeSetNamesPatched && _biomeLoadPatched; if (_biomeMappingRefreshPatched) { ManualLogSource? logger = _logger; if (logger != null) { logger.LogInfo((object)"Expand World Data biome mapping refresh initialized."); } } else if (_patchAttempts >= 15) { ManualLogSource? logger2 = _logger; if (logger2 != null) { logger2.LogWarning((object)("Expand World Data biome mapping refresh is only partially available. Unavailable methods: " + string.Join(", ", list) + ".")); } } } private static void TryPatchBiomeMappingRefreshMethod(Type biomeManagerType, string methodName, Type[]? parameterTypes, HarmonyMethod postfix, ref bool patched, List<string> unavailableMethods) { if (patched || _harmony == null) { return; } try { MethodInfo methodInfo = ((parameterTypes == null) ? AccessTools.Method(biomeManagerType, methodName, (Type[])null, (Type[])null) : AccessTools.Method(biomeManagerType, methodName, parameterTypes, (Type[])null)); if (methodInfo == null) { unavailableMethods.Add(methodName); return; } _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); patched = true; } catch (Exception ex) { unavailableMethods.Add(methodName + " (" + ex.Message + ")"); } } private static void BiomeMappingChangedPostfix() { _biomeMappingRefreshRequested = true; } internal static Type? FindLoadedType(string fullName) { if (LoadedTypes.TryGetValue(fullName, out Type value)) { return value; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Type type; try { type = assembly.GetType(fullName, throwOnError: false); } catch { continue; } if (type != null) { LoadedTypes[fullName] = type; return type; } } return null; } private static void HandleTerrainPrefix(Vector3 pos, float radius, bool isBlueprint, object data) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) string text = GetString(data, "prefab"); float terrainRadius = GetTerrainRadius(radius, isBlueprint, data); if (terrainRadius > 0f) { TerrainMistileSystem.RegisterExternalTerrainIgnoreArea(pos, terrainRadius, "Expand World Data location terrain", 10f); } float noBuildRadius = GetNoBuildRadius(data, Mathf.Max(GetFloat(data, "exteriorRadius"), radius)); float radius2 = Mathf.Max(new float[4] { terrainRadius, radius, GetFloat(data, "exteriorRadius"), noBuildRadius }) + 5f; if (isBlueprint) { TerrainMistileSystem.RegisterProtectedTerrainArea(pos, radius2, "Expand World Data blueprint terrain " + text); } } private static void BlueprintProtectionSyncPostfix() { SyncBlueprintProtectedAreas(); } private static void SyncBlueprintProtectedAreas() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) ZoneSystem instance = ZoneSystem.instance; if (!Object.op_Implicit((Object)(object)instance) || !EnsureBlueprintReflectionMethods()) { return; } List<TerrainMistileSystem.ProtectedTerrainAreaData> list = new List<TerrainMistileSystem.ProtectedTerrainAreaData>(); try { foreach (LocationInstance value in instance.m_locationInstances.Values) { if (value.m_location == null) { continue; } string prefabName = value.m_location.m_prefabName; if (string.IsNullOrWhiteSpace(prefabName)) { continue; } if (!TryIsBlueprintPrefab(prefabName, out var isBlueprint)) { return; } if (isBlueprint) { object locationYaml; bool flag = TryGetLocationYaml(value.m_location, out locationYaml); if (!flag && _tryGetLocationYamlMethod == null) { return; } object obj = (flag ? locationYaml : null); float num = ((obj == null) ? 0f : GetTerrainRadius(value.m_location.m_exteriorRadius, isBlueprint: true, obj)); float num2 = ((obj == null) ? 0f : GetNoBuildRadius(obj, value.m_location.m_exteriorRadius)); float radius = Mathf.Max(new float[3] { num, value.m_location.m_exteriorRadius, num2 }) + 5f; list.Add(new TerrainMistileSystem.ProtectedTerrainAreaData(value.m_position, radius, "Expand World Data blueprint terrain " + prefabName)); } } } catch (Exception ex) { ManualLogSource? logger = _logger; if (logger != null) { logger.LogWarning((object)("Expand World Data blueprint terrain protection refresh failed: " + ex.Message)); } return; } TerrainMistileSystem.ReplaceProtectedTerrainAreas("Expand World Data blueprint terrain", list); } private static bool TryGetLocationYaml(ZoneLocation location, out object? locationYaml) { locationYaml = null; MethodInfo tryGetLocationYamlMethod = _tryGetLocationYamlMethod; if (tryGetLocationYamlMethod == null) { return false; } object[] array = new object[2] { location, null }; try { bool valueOrDefault = tryGetLocationYamlMethod.Invoke(null, array) as bool? == true; locationYaml = array[1]; return valueOrDefault && locationYaml != null; } catch (Exception ex) { _tryGetLocationYamlMethod = null; ManualLogSource? logger = _logger; if (logger != null) { logger.LogWarning((object)("Expand World Data location lookup failed: " + ex.Message)); } return false; } } private static bool TryIsBlueprintPrefab(string prefab, out bool isBlueprint) { isBlueprint = false; MethodInfo isBlueprintPrefabMethod = _isBlueprintPrefabMethod; if (isBlueprintPrefabMethod == null) { return false; } try { isBlueprint = isBlueprintPrefabMethod.Invoke(null, new object[1] { prefab }) as bool? == true; return true; } catch (Exception ex) { _isBlueprintPrefabMethod = null; ManualLogSource? logger = _logger; if (logger != null) { logger.LogWarning((object)("Expand World Data blueprint lookup failed: " + ex.Message)); } return false; } } private static bool EnsureBlueprintReflectionMethods() { if (_blueprintReflectionResolved) { if (_tryGetLocationYamlMethod != null) { return _isBlueprintPrefabMethod != null; } return false; } Type type = FindLoadedType("ExpandWorldData.LocationExtra"); Type type2 = FindLoadedType("ExpandWorldData.LocationYaml"); Type type3 = FindLoadedType("ExpandWorldData.BlueprintManager"); if (type == null || type2 == null || type3 == null) { return false; } _tryGetLocationYamlMethod = AccessTools.Method(type, "TryGetData", new Type[2] { typeof(ZoneLocation), type2.MakeByRefType() }, (Type[])null); _isBlueprintPrefabMethod = AccessTools.Method(type3, "Has", new Type[1] { typeof(string) }, (Type[])null); _blueprintReflectionResolved = true; if (_tryGetLocationYamlMethod != null) { return _isBlueprintPrefabMethod != null; } return false; } internal static float GetTerrainRadius(float exteriorRadius, bool isBlueprint, object data) { string text = GetString(data, "levelArea"); string text2 = GetString(data, "paint"); bool num = ((text.Length == 0) ? isBlueprint : (!text.Equals("false", StringComparison.Ordinal))); float num2 = 0f; if (num) { float num3 = GetFloat(data, "levelRadius"); float num4 = GetFloat(data, "levelBorder"); num2 = Mathf.Max(num2, (num3 == 0f && num4 == 0f) ? exteriorRadius : (num3 + num4)); } if (text2.Length > 0) { float num5 = GetNullableFloat(data, "paintRadius") ?? exteriorRadius; float num6 = GetNullableFloat(data, "paintBorder") ?? 5f; num2 = Mathf.Max(num2, num5 + num6); } return num2; } private static float GetNoBuildRadius(object data, float exteriorRadius) { string text = GetString(data, "noBuild").Trim(); if (text.Length == 0 || text.Equals("false", StringComparison.OrdinalIgnoreCase)) { return 0f; } if (text.Equals("true", StringComparison.OrdinalIgnoreCase)) { return exteriorRadius; } if (!float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return 0f; } return Mathf.Max(0f, result); } private static string GetString(object data, string fieldName) { return (GetField(data, fieldName)?.GetValue(data) as string) ?? ""; } private static float GetFloat(object data, string fieldName) { object obj = GetField(data, fieldName)?.GetValue(data); if (obj is float) { return (float)obj; } return 0f; } private static float? GetNullableFloat(object data, string fieldName) { object obj = GetField(data, fieldName)?.GetValue(data); if (!(obj is float)) { return null; } return (float)obj; } private static FieldInfo? GetField(object data, string fieldName) { Type type = data.GetType(); if (!FieldsByType.TryGetValue(type, out Dictionary<string, FieldInfo> value)) { value = new Dictionary<string, FieldInfo>(StringComparer.Ordinal); FieldsByType[type] = value; } if (!value.TryGetValue(fieldName, out var value2)) { value2 = (value[fieldName] = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)); } return value2; } } internal static class TerrainMistileLocalization { private const string FileExtension = ".yml"; private static readonly string FilePrefix = "TerrainMistile."; private static readonly IDeserializer Deserializer = new DeserializerBuilder().IgnoreFields().Build(); internal static void Load(ManualLogSource logger) { CustomLocalization localization = LocalizationManager.Instance.GetLocalization(); bool flag = TryLoadEmbeddedYaml(localization, "English", logger); TryLoadEmbeddedYaml(localization, "Korean", logger); LoadExternalYamlFiles(localization, logger); if (!flag) { logger.LogWarning((object)"Embedded English localization was not found or was invalid. External localization files may still provide a fallback."); } } private static bool TryLoadEmbeddedYaml(CustomLocalization localization, string language, ManualLogSource logger) { string resourceSuffix = ".Translations.TerrainMistile." + language + ".yml"; Assembly executingAssembly = Assembly.GetExecutingAssembly(); string text = executingAssembly.GetManifestResourceNames().FirstOrDefault((string name) => name.EndsWith(resourceSuffix, StringComparison.Ordinal)); if (text == null) { logger.LogWarning((object)("Embedded localization resource not found: Translations/TerrainMistile." + language + ".yml")); return false; } try { using Stream stream = executingAssembly.GetManifestResourceStream(text); if (stream == null) { logger.LogWarning((object)("Embedded localization resource could not be opened: " + text)); return false; } using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true); return TryAddYamlTranslations(localization, language, streamReader.ReadToEnd(), "embedded TerrainMistile." + language + ".yml", logger); } catch (Exception ex) { logger.LogWarning((object)("Could not load embedded " + language + " localization: " + ex.Message)); return false; } } private static void LoadExternalYamlFiles(CustomLocalization localization, ManualLogSource logger) { string bepInExRootPath = Paths.BepInExRootPath; string[] files; try { files = Directory.GetFiles(bepInExRootPath, FilePrefix + "*.yml", SearchOption.AllDirectories); } catch (Exception ex) { logger.LogWarning((object)("Could not search for TerrainMistile localization files: " + ex.Message)); return; } string[] array = files.OrderBy((string path) => IsInsideDirectory(path, Paths.ConfigPath) ? 1 : 0).ThenBy<string, string>((string path) => path, StringComparer.Ordinal).ToArray(); Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal); string[] array2 = array; foreach (string text in array2) { if (!TryGetLanguageFromFileName(text, out string language)) { logger.LogWarning((object)("Ignoring localization file with an invalid name: " + text + ". Expected TerrainMistile.<Language>.yml.")); continue; } string yaml; try { yaml = File.ReadAllText(text, Encoding.UTF8); } catch (Exception ex2) { logger.LogWarning((object)("Could not read " + language + " localization from " + text + ": " + ex2.Message)); continue; } if (dictionary.TryGetValue(language, out var value)) { logger.LogWarning((object)("Loading another " + language + " localization file from " + text + ". Its duplicate tokens override values loaded from " + value + ".")); } if (TryAddYamlTranslations(localization, language, yaml, text, logger)) { dictionary[language] = text; } } } private static bool TryAddYamlTranslations(CustomLocalization localization, string language, string yaml, string source, ManualLogSource logger) { if (!TryParseYaml(yaml, out Dictionary<string, string> translations, out string error)) { logger.LogWarning((object)("Could not load " + language + " localization from " + source + ": " + error)); return false; } try { localization.AddTranslation(ref language, translations); logger.LogInfo((object)$"Loaded {translations.Count} {language} localization entries from {source}."); return true; } catch (Exception ex) { logger.LogWarning((object)("Could not register " + language + " localization from " + source + ": " + ex.Message)); return false; } } internal static bool TryGetLanguageFromFileName(string path, out string language) { language = string.Empty; string fileName = Path.GetFileName(path); if (!fileName.StartsWith(FilePrefix, StringComparison.Ordinal) || !fileName.EndsWith(".yml", StringComparison.Ordinal)) { return false; } int num = fileName.Length - FilePrefix.Length - ".yml".Length; if (num <= 0) { return false; } language = fileName.Substring(FilePrefix.Length, num); if (language.IndexOf('.') >= 0 || !char.IsUpper(language[0])) { language = string.Empty; return false; } return true; } internal static bool TryParseYaml(string yaml, out Dictionary<string, string> translations, out string error) { translations = new Dictionary<string, string>(StringComparer.Ordinal); error = string.Empty; try { Dictionary<string, string> dictionary = Deserializer.Deserialize<Dictionary<string, string>>(yaml); if (dictionary == null || dictionary.Count == 0) { error = "The localization file is empty."; return false; } foreach (KeyValuePair<string, string> item in dictionary) { string text = item.Key?.Trim() ?? string.Empty; if (text.Length == 0) { error = "Localization keys must not be empty."; return false; } if (item.Value == null) { error = "Localization value for '" + text + "' must not be null."; return false; } if (translations.ContainsKey(text)) { error = "Localization key '" + text + "' is duplicated."; return false; } translations.Add(text, item.Value); } return true; } catch (Exception ex) { error = ex.Message; return false; } } private static bool IsInsideDirectory(string path, string directory) { string text = Path.GetFullPath(directory).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); char directorySeparatorChar = Path.DirectorySeparatorChar; string value = text + directorySeparatorChar; return Path.GetFullPath(path).StartsWith(value, StringComparison.OrdinalIgnoreCase); } } [HarmonyPatch(typeof(ZoneSystem), "SpawnLocation", new Type[] { typeof(ZoneLocation), typeof(int), typeof(Vector3), typeof(Quaternion), typeof(SpawnMode), typeof(List<GameObject>) })] internal static class ZoneSystemSpawnLocationPatch { private static void Prefix(ZoneLocation location, Vector3 pos) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) TerrainMistileSystem.RegisterLocationTerrainLoadGrace(location, pos); } } [HarmonyPatch(typeof(ZNetScene), "Destroy", new Type[] { typeof(GameObject) })] internal static class ZNetSceneDestroyPatch { private static void Prefix(GameObject __0) { if (Object.op_Implicit((Object)(object)__0)) { TerrainMistileBehaviour component = __0.GetComponent<TerrainMistileBehaviour>(); if (Object.op_Implicit((Object)(object)component)) { component.TryResetTerrain(); } } } } [HarmonyPatch(typeof(ZNet), "Shutdown")] internal static class ZNetShutdownPatch { private static void Postfix() { TerrainMistileSystem.ClearWorldState(); } } internal static class TerrainMistilePrefab { internal const string PrefabName = "TerrainMistile"; internal const string EnforcerPrefabName = "TerrainMistileEnforcer"; private const string SourcePrefabName = "Mistile"; private static bool _hooked; private static bool _terrainMistileRegistered; private static bool _enforcerRegistered; private static GameObject? _registeredPrefab; private static GameObject? _registeredEnforcerPrefab; private static readonly List<Material> RegisteredPrefabVisualMaterials = new List<Material>(); private static readonly List<Material> RegisteredEnforcerPrefabVisualMaterials = new List<Material>(); internal static void RegisterPrefabHook() { if (!_hooked) { PrefabManager.OnVanillaPrefabsAvailable += CreateTerrainMistilePrefab; _hooked = true; } } internal static void UnregisterPrefabHook() { if (_hooked) { PrefabManager.OnVanillaPrefabsAvailable -= CreateTerrainMistilePrefab; _hooked = false; } } private static void CreateTerrainMistilePrefab() { if (!_terrainMistileRegistered && TryCreateTerrainMistilePrefab("TerrainMistile", enforcer: false, RegisteredPrefabVisualMaterials, out GameObject registeredPrefab)) { _registeredPrefab = registeredPrefab; _terrainMistileRegistered = true; } if (!_enforcerRegistered && TryCreateTerrainMistilePrefab("TerrainMistileEnforcer", enforcer: true, RegisteredEnforcerPrefabVisualMaterials, out GameObject registeredPrefab2)) { _registeredEnforcerPrefab = registeredPrefab2; _enforcerRegistered = true; } } private static bool TryCreateTerrainMistilePrefab(string prefabName, bool enforcer, List<Material> registeredVisualMaterials, out GameObject? registeredPrefab) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) registeredPrefab = null; GameObject val = PrefabManager.Instance.CreateClonedPrefab(prefabName, "Mistile"); if (!Object.op_Implicit((Object)(object)val)) { TerrainMistilePlugin.TerrainMistileLogger.LogError((object)("Could not clone vanilla prefab 'Mistile' as '" + prefabName + "'.")); return false; } Character component = val.GetComponent<Character>(); if (Object.op_Implicit((Object)(object)component)) { component.m_faction = (Faction)8; component.m_aiSkipTarget = true; } MonsterAI component2 = val.GetComponent<MonsterAI>(); if (Object.op_Implicit((Object)(object)component2)) { component2.m_enableHuntPlayer = false; component2.m_attackPlayerObjects = false; ((BaseAI)component2).m_aggravatable = false; component2.m_alertRange = 0f; ((BaseAI)component2).m_viewRange = 0f; ((BaseAI)component2).m_hearRange = 0f; } Humanoid component3 = val.GetComponent<Humanoid>(); if (Object.op_Implicit((Object)(object)component3)) { component3.m_defaultItems = Array.Empty<GameObject>(); } TerrainMistileBehaviour terrainMistileBehaviour = val.GetComponent<TerrainMistileBehaviour>(); if (!Object.op_Implicit((Object)(object)terrainMistileBehaviour)) { terrainMistileBehaviour = val.AddComponent<TerrainMistileBehaviour>(); } terrainMistileBehaviour.ConfigureEnforcer(enforcer); if (Object.op_Implicit((Object)(object)component)) { component.m_name = (enforcer ? "$terrainmistile_enforcer_name" : "$terrainmistile_creature_name"); } MakeCollidersNonBlocking(val); ApplyVisuals(val, TerrainMistileSpawnRules.DefaultVisualColor, registeredVisualMaterials); PrefabManager.Instance.AddPrefab(val); registeredPrefab = val; TerrainMistilePlugin.TerrainMistileLogger.LogInfo((object)("Registered '" + prefabName + "' prefab from 'Mistile'.")); return true; } internal static void RefreshRegisteredPrefabVisuals() { RefreshRegisteredPrefabVisuals("TerrainMistile", _registeredPrefab, RegisteredPrefabVisualMaterials); RefreshRegisteredPrefabVisuals("TerrainMistileEnforcer", _registeredEnforcerPrefab, RegisteredEnforcerPrefabVisualMaterials); } private static void RefreshRegisteredPrefabVisuals(string prefabName, GameObject? registeredPrefab, List<Material> registeredVisualMaterials) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) GameObject val = (GameObject)(Object.op_Implicit((Object)(object)registeredPrefab) ? ((object)registeredPrefab) : ((object)(Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab(prefabName) : null))); if (Object.op_Implicit((Object)(object)val)) { RefreshRegisteredPrefabVisuals(val, TerrainMistileSpawnRules.DefaultVisualColor, registeredVisualMaterials); } } internal static void ApplyVisuals(GameObject root, Color color, List<Material>? ownedMaterials = null) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)root)) { Array materials = ((ownedMaterials != null && ownedMaterials.Count > 0) ? ((Array)ownedMaterials.ToArray()) : ((Array)Array.Empty<Material>())); ownedMaterials?.Clear(); ApplyLightColor(root, color); ApplyParticleColor(root, color); ApplyMaterialColor(root, color, ownedMaterials); ReleaseVisualMaterials((Material[])materials); } } internal static void ReleaseVisualMaterials(List<Material> materials) { foreach (Material material in materials) { if (Object.op_Implicit((Object)(object)material)) { Object.Destroy((Object)(object)material); } } materials.Clear(); } private static void ReleaseVisualMaterials(Material[] materials) { foreach (Material val in materials) { if (Object.op_Implicit((Object)(object)val)) { Object.Destroy((Object)(object)val); } } } private static void RefreshRegisteredPrefabVisuals(GameObject root, Color color, List<Material> registeredVisualMaterials) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) ApplyLightColor(root, color); ApplyParticleColor(root, color); for (int num = registeredVisualMaterials.Count - 1; num >= 0; num--) { Material val = registeredVisualMaterials[num]; if (!Object.op_Implicit((Object)(object)val)) { registeredVisualMaterials.RemoveAt(num); } else { ApplyMaterialProperties(val, color); } } if (registeredVisualMaterials.Count == 0) { ApplyMaterialColor(root, color, registeredVisualMaterials); } } internal static void MakeCollidersNonBlocking(GameObject root) { if (!Object.op_Implicit((Object)(object)root)) { return; } Collider[] componentsInChildren = root.GetComponentsInChildren<Collider>(true); foreach (Collider val in componentsInChildren) { if (Object.op_Implicit((Object)(object)val)) { val.isTrigger = true; } } } private static void ApplyLightColor(GameObject root, Color color) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) Light[] componentsInChildren = root.GetComponentsInChildren<Light>(true); foreach (Light val in componentsInChildren) { val.color = WithAlpha(color, val.color.a); } } private static void ApplyParticleColor(GameObject root, Color color) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) ParticleSystem[] componentsInChildren = root.GetComponentsInChildren<ParticleSystem>(true); foreach (ParticleSystem obj in componentsInChildren) { MainModule main = obj.main; ((MainModule)(ref main)).startColor = Recolor(((MainModule)(ref main)).startColor, color); ColorOverLifetimeModule colorOverLifetime = obj.colorOverLifetime; if (((ColorOverLifetimeModule)(ref colorOverLifetime)).enabled) { ((ColorOverLifetimeModule)(ref colorOverLifetime)).color = Recolor(((ColorOverLifetimeModule)(ref colorOverLifetime)).color, color); } } } private static MinMaxGradient Recolor(MinMaxGradient source, Color color) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected I4, but got Unknown //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: 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_00bb: 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_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) ParticleSystemGradientMode mode = ((MinMaxGradient)(ref source)).mode; return (MinMaxGradient)((int)mode switch { 0 => new MinMaxGradient(WithAlpha(color, ((MinMaxGradient)(ref source)).color.a)), 2 => new MinMaxGradient(WithAlpha(ScaleRgb(color, 0.65f), ((MinMaxGradient)(ref source)).colorMin.a), WithAlpha(ScaleRgb(color, 1.15f), ((MinMaxGradient)(ref source)).colorMax.a)), 1 => new MinMaxGradient(CreateGradient(((MinMaxGradient)(ref source)).gradient, color)), 3 => new MinMaxGradient(CreateGradient(((MinMaxGradient)(ref source)).gradientMin, color), CreateGradient(((MinMaxGradient)(ref source)).gradientMax, color)), _ => new MinMaxGradient(color), }); } private static Gradient CreateGradient(Gradient source, Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) Gradient val = new Gradient(); GradientColorKey[] array = source.colorKeys; GradientAlphaKey[] array2 = source.alphaKeys; if (array == null || array.Length == 0) { array = (GradientColorKey[])(object)new GradientColorKey[2] { new GradientColorKey(color, 0f), new GradientColorKey(color, 1f) }; } GradientColorKey[] array3 = (GradientColorKey[])(object)new GradientColorKey[array.Length]; for (int i = 0; i < array.Length; i++) { array3[i] = new GradientColorKey(GetGradientShade(color, i, array.Length), array[i].time); } if (array2 == null || array2.Length == 0) { array2 = (GradientAlphaKey[])(object)new GradientAlphaKey[2] { new GradientAlphaKey(color.a, 0f), new GradientAlphaKey(0f, 1f) }; } val.SetKeys(array3, array2); val.mode = source.mode; return val; } private static Color GetGradientShade(Color color, int index, int count) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) float num = ((count <= 1) ? 0f : ((float)index / (float)(count - 1))); return ScaleRgb(color, Mathf.Lerp(1.3f, 0.7f, num)); } private static void ApplyMaterialColor(GameObject root, Color color, List<Material>? ownedMaterials) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>(true); foreach (Renderer val in componentsInChildren) { Material[] sharedMaterials = val.sharedMaterials; bool flag = false; for (int j = 0; j < sharedMaterials.Length; j++) { Material val2 = sharedMaterials[j]; if (Object.op_Implicit((Object)(object)val2) && ShouldColorMaterial(val, val2)) { Material val3 = Object.Instantiate<Material>(val2); ApplyMaterialProperties(val3, color); sharedMaterials[j] = val3; ownedMaterials?.Add(val3); flag = true; } } if (flag) { val.sharedMaterials = sharedMaterials; } } } private static bool ShouldColorMaterial(Renderer renderer, Material material) { string value = (Object.op_Implicit((Object)(object)((Component)renderer).gameObject) ? ((Object)((Component)renderer).gameObject).name : ""); string value2 = (Object.op_Implicit((Object)(object)material) ? ((Object)material).name : ""); if (!ContainsAny(value, "flame", "flare", "spark", "ember")) { return ContainsAny(value2, "flame", "spark", "glow", "pixel_unlit"); } return true; } private static void ApplyMaterialProperties(Material material, Color color) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) SetMaterialColor(material, "_TintColor", color, preserveIntensity: false); SetMaterialColor(material, "_Color", color, preserveIntensity: false); SetMaterialColor(material, "_EmissionColor", color, preserveIntensity: true); } private static void SetMaterialColor(Material material, string property, Color color, bool preserveIntensity) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (material.HasProperty(property)) { Color color2 = material.GetColor(property); float scale = (preserveIntensity ? Mathf.Max(new float[4] { 1f, color2.r, color2.g, color2.b }) : 1f); material.SetColor(property, WithAlpha(ScaleRgb(color, scale), color2.a)); } } private static bool ContainsAny(string value, params string[] needles) { foreach (string value2 in needles) { if (value.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } private static Color ScaleRgb(Color color, float scale) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) return new Color(color.r * scale, color.g * scale, color.b * scale, color.a); } private static Color WithAlpha(Color color, float alpha) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) color.a = alpha; return color; } } internal static class TerrainMistileSpawnRules { private sealed class TerrainMistileSpawnRuleValues { public float? Interval { get; set; } public float? PlayerSearchRadius { get; set; } public float? SpawnChance { get; set; } public float? MaxDeformationSpawnChanceBonus { get; set; } public int? MaxSpawn { get; set; } public bool? PerPlayerSpawn { get; set; } public int? PlayerBaseValue { get; set; } public float? BaseCheckRadius { get; set; } public string? SpawnRadius { get; set; } public float? SpawnAltitude { get; set; } public float? ResetRadius { get; set; } public float? Health { get; set; } public string? VisualColor { get; set; } } private const float DefaultInterval = 60f; private const float DefaultPlayerSearchRadius = 32f; private const float DefaultSpawnChance = 0.25f; private const float DefaultMaxDeformationSpawnChanceBonus = 0.25f; private const bool DefaultPerPlayerSpawn = true; private const int DefaultPlayerBaseValue = 1; private const float DefaultBaseCheckRadius = 24f; private const int DefaultMaxSpawn = 3; private const float DefaultSpawnRadiusMin = 16f; private const float DefaultSpawnRadiusMax = 32f; private const float DefaultSpawnAltitude = 8f; private const float DefaultResetRadius = 8f; private const float DefaultHealth = 1f; internal const float MaximumResetRadius = 64f; internal const string DefaultVisualColorHex = "#45FF5A"; private static readonly Color FallbackVisualColor = new Color(23f / 85f, 1f, 0.3529412f, 1f); private static readonly string[] DefaultPlayerBasePrefabNames = new string[40] { "ashwood_bed", "bed", "blackforge", "blastfurnace", "BogWitch_Fire_Pit", "bonfire", "charcoal_kiln", "charred_shieldgenerator", "dverger_guardstone", "eitrrefinery", "fermenter", "fire_pit", "fire_pit_haldor", "fire_pit_hildir", "fire_pit_iron", "forge", "guard_stone", "hearth", "piece_artisanstation", "piece_bed02", "piece_brazierceiling01", "piece_brazierfloor01", "piece_brazierfloor02", "piece_groundtorch", "piece_groundtorch_blue", "piece_groundtorch_green", "piece_groundtorch_mist", "piece_groundtorch_wood", "piece_magetable", "piece_oven", "piece_shieldgenerator", "piece_spinningwheel", "piece_stonecutter", "piece_walltorch", "piece_workbench", "portal", "portal_stone", "portal_wood", "smelter", "windmill" }; private static readonly IDeserializer Deserializer = new DeserializerBuilder().IgnoreUnmatchedProperties().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); private static readonly Dictionary<int, TerrainMistileBiomeSpawnRule> BiomeRules = new Dictionary<int, TerrainMistileBiomeSpawnRule>(); private static TerrainMistileBiomeSpawnRule _defaultRule = CreateDefaultRule(); private static HashSet<string> _playerBasePrefabNames = CreateDefaultPlayerBasePrefabNames(); private static float _maxPlayerSearchRadius = 32f; private static float _maxResetRadius = 8f; private static bool _hasEnabledRules = true; private static ManualLogSource? _logger; internal static float MaxPlayerSearchRadius => _maxPlayerSearchRadius; internal static float MaxResetRadius => _maxResetRadius; internal static bool HasEnabledRules => _hasEnabledRules; internal static Color DefaultVisualColor => _defaultRule.VisualColor; internal static TerrainMistileBiomeSpawnRule DefaultRule => _defaultRule; internal static bool IsPlayerBasePrefabName(string prefabName) { return _playerBasePrefabNames.Contains(prefabName); } internal static List<string> GetPlayerBasePrefabNamesSnapshot() { return new List<string>(_playerBasePrefabNames); } internal static List<int> GetConfiguredBiomeKeysSnapshot() { return new List<int>(BiomeRules.Keys); } internal static void Initialize(ManualLogSource logger) { _logger = logger; } internal static void EnsureFileExists(string path) { if (!File.Exists(path)) { string directoryName = Path.GetDirectoryName(path); if (!string.IsNullOrWhiteSpace(directoryName)) { Directory.CreateDirectory(directoryName); } File.WriteAllText(path, BuildDefaultYaml()); } } internal static bool LoadYamlText(string yaml, string source) { if (!TryParseYaml(yaml, out TerrainMistileBiomeSpawnRule defaultRule, out Dictionary<int, TerrainMistileBiomeSpawnRule> parsedRules, out HashSet<string> playerBasePrefabNames, out string error)) { ManualLogSource? logger = _logger; if (logger != null) { logger.LogError((object)("Failed to parse TerrainMistile spawn rules from " + source + ": " + error)); } return false; } _defaultRule = defaultRule; _playerBasePrefabNames = playerBasePrefabNames; BiomeRules.Clear(); foreach (KeyValuePair<int, TerrainMistileBiomeSpawnRule> item in parsedRules) { BiomeRules[item.Key] = item.Value; } RecalculateRuntimeState(); ManualLogSource? logger2 = _logger; if (logger2 != null) { logger2.LogInfo((object)$"Loaded TerrainMistile spawn rules from {source}. Default={_defaultRule}; overrides={BiomeRules.Count}; playerBasePrefabs={_playerBasePrefabNames.Count}; enabled={_hasEnabledRules}."); } return true; } internal static bool TryGetEnabledRule(int biome, out TerrainMistileBiomeSpawnRule rule) { rule = GetRule(biome); if (biome != 0) { return rule.Enabled; } return false; } internal static TerrainMistileBiomeSpawnRule GetRule(int biome) { if (!BiomeRules.TryGetValue(biome, out var value)) { return _defaultRule; } return value; } internal static int GetBiomeKey(Vector3 point) { //IL_0000: