Please disclose if your mod was created primarily 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 NoCapLumber v1.0.0
NoCapLumber.dll
Decompiled 3 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.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using UnityEngine.UI; 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.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("NoCapLumber")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyProduct("NoCapLumber")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace LocalizationManager { [PublicAPI] public class Localizer { private static readonly Dictionary<string, Dictionary<string, Func<string>>> PlaceholderProcessors; private static readonly Dictionary<string, Dictionary<string, string>> loadedTexts; private static readonly ConditionalWeakTable<Localization, string> localizationLanguage; private static readonly List<WeakReference<Localization>> localizationObjects; private static BaseUnityPlugin? _plugin; private static readonly List<string> fileExtensions; private static BaseUnityPlugin plugin { get { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Expected O, but got Unknown if (_plugin == null) { IEnumerable<TypeInfo> source; try { source = Assembly.GetExecutingAssembly().DefinedTypes.ToList(); } catch (ReflectionTypeLoadException ex) { source = from t in ex.Types where t != null select t.GetTypeInfo(); } _plugin = (BaseUnityPlugin)Chainloader.ManagerObject.GetComponent((Type)source.First((TypeInfo t) => t.IsClass && typeof(BaseUnityPlugin).IsAssignableFrom(t))); } return _plugin; } } public static event Action? OnLocalizationComplete; private static void UpdatePlaceholderText(Localization localization, string key) { localizationLanguage.TryGetValue(localization, out string value); string text = loadedTexts[value][key]; if (PlaceholderProcessors.TryGetValue(key, out Dictionary<string, Func<string>> value2)) { text = value2.Aggregate(text, (string current, KeyValuePair<string, Func<string>> kv) => current.Replace("{" + kv.Key + "}", kv.Value())); } localization.AddWord(key, text); } public static void AddPlaceholder<T>(string key, string placeholder, ConfigEntry<T> config, Func<T, string>? convertConfigValue = null) where T : notnull { string key2 = key; string placeholder2 = placeholder; Func<T, string> convertConfigValue2 = convertConfigValue; ConfigEntry<T> config2 = config; if (convertConfigValue2 == null) { convertConfigValue2 = (T val) => val.ToString(); } if (!PlaceholderProcessors.ContainsKey(key2)) { PlaceholderProcessors[key2] = new Dictionary<string, Func<string>>(); } config2.SettingChanged += delegate { UpdatePlaceholder(); }; if (loadedTexts.ContainsKey(Localization.instance.GetSelectedLanguage())) { UpdatePlaceholder(); } void UpdatePlaceholder() { PlaceholderProcessors[key2][placeholder2] = () => convertConfigValue2(config2.Value); UpdatePlaceholderText(Localization.instance, key2); } } public static void AddText(string key, string text) { List<WeakReference<Localization>> list = new List<WeakReference<Localization>>(); foreach (WeakReference<Localization> localizationObject in localizationObjects) { if (localizationObject.TryGetTarget(out var target)) { Dictionary<string, string> dictionary = loadedTexts[localizationLanguage.GetOrCreateValue(target)]; if (!target.m_translations.ContainsKey(key)) { dictionary[key] = text; target.AddWord(key, text); } } else { list.Add(localizationObject); } } foreach (WeakReference<Localization> item in list) { localizationObjects.Remove(item); } } public static void Load() { _ = plugin; } public static void LoadLocalizationLater(Localization __instance) { LoadLocalization(Localization.instance, __instance.GetSelectedLanguage()); } public static void SafeCallLocalizeComplete() { Localizer.OnLocalizationComplete?.Invoke(); } private static void LoadLocalization(Localization __instance, string language) { if (!localizationLanguage.Remove(__instance)) { localizationObjects.Add(new WeakReference<Localization>(__instance)); } localizationLanguage.Add(__instance, language); Dictionary<string, string> dictionary = new Dictionary<string, string>(); foreach (string item in from f in Directory.GetFiles(Path.GetDirectoryName(Paths.PluginPath), plugin.Info.Metadata.Name + ".*", SearchOption.AllDirectories) where fileExtensions.IndexOf(Path.GetExtension(f)) >= 0 select f) { string[] array = Path.GetFileNameWithoutExtension(item).Split(new char[1] { '.' }); if (array.Length >= 2) { string text = array[1]; if (dictionary.ContainsKey(text)) { Debug.LogWarning((object)("Duplicate key " + text + " found for " + plugin.Info.Metadata.Name + ". The duplicate file found at " + item + " will be skipped.")); } else { dictionary[text] = item; } } } byte[] array2 = LoadTranslationFromAssembly("English"); if (array2 == null) { throw new Exception("Found no English localizations in mod " + plugin.Info.Metadata.Name + ". Expected an embedded resource translations/English.json or translations/English.yml."); } Dictionary<string, string> dictionary2 = new DeserializerBuilder().IgnoreFields().Build().Deserialize<Dictionary<string, string>>(Encoding.UTF8.GetString(array2)); if (dictionary2 == null) { throw new Exception("Localization for mod " + plugin.Info.Metadata.Name + " failed: Localization file was empty."); } string text2 = null; if (language != "English") { if (dictionary.TryGetValue(language, out var value)) { text2 = File.ReadAllText(value); } else { byte[] array3 = LoadTranslationFromAssembly(language); if (array3 != null) { text2 = Encoding.UTF8.GetString(array3); } } } if (text2 == null && dictionary.TryGetValue("English", out var value2)) { text2 = File.ReadAllText(value2); } if (text2 != null) { foreach (KeyValuePair<string, string> item2 in new DeserializerBuilder().IgnoreFields().Build().Deserialize<Dictionary<string, string>>(text2) ?? new Dictionary<string, string>()) { dictionary2[item2.Key] = item2.Value; } } loadedTexts[language] = dictionary2; foreach (KeyValuePair<string, string> item3 in dictionary2) { UpdatePlaceholderText(__instance, item3.Key); } } static Localizer() { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected O, but got Unknown //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Expected O, but got Unknown PlaceholderProcessors = new Dictionary<string, Dictionary<string, Func<string>>>(); loadedTexts = new Dictionary<string, Dictionary<string, string>>(); localizationLanguage = new ConditionalWeakTable<Localization, string>(); localizationObjects = new List<WeakReference<Localization>>(); fileExtensions = new List<string>(2) { ".json", ".yml" }; Harmony val = new Harmony("org.bepinex.helpers.LocalizationManager"); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Localization), "SetupLanguage", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalization", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "SetupGui", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalizationLater", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "Start", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "SafeCallLocalizeComplete", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static byte[]? LoadTranslationFromAssembly(string language) { foreach (string fileExtension in fileExtensions) { byte[] array = ReadEmbeddedFileBytes("translations." + language + fileExtension); if (array != null) { return array; } } return null; } public static byte[]? ReadEmbeddedFileBytes(string resourceFileName, Assembly? containingAssembly = null) { string resourceFileName2 = resourceFileName; using MemoryStream memoryStream = new MemoryStream(); if ((object)containingAssembly == null) { containingAssembly = Assembly.GetCallingAssembly(); } string text = containingAssembly.GetManifestResourceNames().FirstOrDefault((string str) => str.EndsWith(resourceFileName2, StringComparison.Ordinal)); if (text != null) { containingAssembly.GetManifestResourceStream(text)?.CopyTo(memoryStream); } return (memoryStream.Length == 0L) ? null : memoryStream.ToArray(); } } public static class LocalizationManagerVersion { public const string Version = "1.4.1"; } } namespace NoCapLumber { [HarmonyPatch(typeof(Attack), "DoMeleeAttack")] public static class AxeTreeComboPatch { [HarmonyPrefix] private static void Prefix(Attack __instance) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Invalid comparison between Unknown and I4 //IL_0065: 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) //IL_006d: Unknown result type (might be due to invalid IL or missing references) if (NoCapLumberPlugin._enableAxeComboOnTrees.Value.IsOn()) { ItemData weapon = __instance.GetWeapon(); if (weapon?.m_shared != null && (int)weapon.m_shared.m_skillType == 7 && (__instance.m_resetChainIfHit & 2) != 0) { __instance.m_resetChainIfHit = (DestructibleType)(__instance.m_resetChainIfHit & -3); } } } } public static class NextSwingLungeState { private sealed class CharacterState { public int PendingSwingCount; public float PendingExpireAt; public int LastStartedChainLevel = -1; public bool LastStartedWasAxe; public float LastStartedAt; } private sealed class AttackState { public float LungeMultiplier = 1f; } private static readonly ConditionalWeakTable<Character, CharacterState> CharacterStates = new ConditionalWeakTable<Character, CharacterState>(); private static readonly ConditionalWeakTable<Attack, AttackState> AttackStates = new ConditionalWeakTable<Attack, AttackState>(); private const float PendingDurationSeconds = 1.25f; private const float ComboReturnWindowSeconds = 1.5f; private const int PendingSwingCountPerTreeChopHit = 1; public static void MarkPending(Character attacker) { if (!((Object)(object)attacker == (Object)null)) { CharacterState orCreateValue = CharacterStates.GetOrCreateValue(attacker); orCreateValue.PendingSwingCount = Math.Max(orCreateValue.PendingSwingCount, 1); orCreateValue.PendingExpireAt = Time.time + 1.25f; } } public static bool HasPending(Character attacker) { if ((Object)(object)attacker == (Object)null) { return false; } if (!CharacterStates.TryGetValue(attacker, out CharacterState value)) { return false; } if (Time.time > value.PendingExpireAt) { value.PendingSwingCount = 0; return false; } return value.PendingSwingCount > 0; } public static bool TryConsumePending(Character attacker) { if ((Object)(object)attacker == (Object)null) { return false; } if (!CharacterStates.TryGetValue(attacker, out CharacterState value)) { return false; } if (Time.time > value.PendingExpireAt) { value.PendingSwingCount = 0; return false; } if (value.PendingSwingCount <= 0) { return false; } value.PendingSwingCount--; return true; } public static bool IsComboReturnFirstSwing(Character attacker, int currentChainLevel, bool currentIsAxe) { if ((Object)(object)attacker == (Object)null || !currentIsAxe || currentChainLevel < 0) { return false; } if (!CharacterStates.TryGetValue(attacker, out CharacterState value)) { return false; } if (!value.LastStartedWasAxe || value.LastStartedChainLevel < 0) { return false; } if (Time.time - value.LastStartedAt > 1.5f) { return false; } return value.LastStartedChainLevel > currentChainLevel; } public static void NoteSwingStart(Character attacker, int chainLevel, bool isAxe) { if (!((Object)(object)attacker == (Object)null)) { CharacterState orCreateValue = CharacterStates.GetOrCreateValue(attacker); orCreateValue.LastStartedChainLevel = chainLevel; orCreateValue.LastStartedWasAxe = isAxe; orCreateValue.LastStartedAt = Time.time; } } public static void SetAttackLungeMultiplier(Attack attack, float lungeMultiplier) { if (attack != null) { AttackStates.GetOrCreateValue(attack).LungeMultiplier = Mathf.Clamp01(lungeMultiplier); } } public static bool TryGetAttackLungeMultiplier(Attack attack, out float lungeMultiplier) { lungeMultiplier = 1f; if (attack == null) { return false; } if (!AttackStates.TryGetValue(attack, out AttackState value)) { return false; } lungeMultiplier = value.LungeMultiplier; return true; } public static void ClearAttack(Attack attack) { if (attack != null) { AttackStates.Remove(attack); } } } [HarmonyPatch(typeof(Attack))] public static class NextSwingLungeAttackPatch { private static readonly RaycastHit[] TreeCheckHits = (RaycastHit[])(object)new RaycastHit[64]; private const float SuppressedLungeMultiplier = 0f; [HarmonyPatch("Start", new Type[] { typeof(Humanoid), typeof(Rigidbody), typeof(ZSyncAnimation), typeof(CharacterAnimEvent), typeof(VisEquipment), typeof(ItemData), typeof(Attack), typeof(float), typeof(float) })] [HarmonyPostfix] private static void StartPostfix(bool __result, Attack __instance, Humanoid character, ItemData weapon) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Invalid comparison between Unknown and I4 if (!__result) { return; } int currentAttackCainLevel = __instance.m_currentAttackCainLevel; bool flag = weapon?.m_shared != null && (int)weapon.m_shared.m_skillType == 7; bool flag2 = NextSwingLungeState.IsComboReturnFirstSwing((Character)(object)character, currentAttackCainLevel, flag); try { if (!NoCapLumberPlugin._enableNextSwingLungeSuppressionAfterTreeChop.Value.IsOff()) { float num = 0f; if (flag && (currentAttackCainLevel > 0 || flag2) && NextSwingLungeState.HasPending((Character)(object)character) && (flag2 || IsLikelyWoodcuttingTarget(character, __instance.m_attackRange)) && NextSwingLungeState.TryConsumePending((Character)(object)character)) { __instance.m_speedFactor *= num; NextSwingLungeState.SetAttackLungeMultiplier(__instance, num); } } } finally { NextSwingLungeState.NoteSwingStart((Character)(object)character, currentAttackCainLevel, flag); } } [HarmonyPatch("Stop")] [HarmonyPrefix] private static void StopPrefix(Attack __instance) { NextSwingLungeState.ClearAttack(__instance); } private static bool IsLikelyWoodcuttingTarget(Humanoid character, float attackRange) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_003c: 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) if ((Object)(object)character == (Object)null) { return false; } Vector3 centerPoint = ((Character)character).GetCenterPoint(); Vector3 forward = ((Component)character).transform.forward; float num = Mathf.Max(1.5f, attackRange + 0.75f); int num2 = Physics.SphereCastNonAlloc(centerPoint, 1f, forward, TreeCheckHits, num, -1, (QueryTriggerInteraction)1); for (int i = 0; i < num2; i++) { Collider collider = ((RaycastHit)(ref TreeCheckHits[i])).collider; if (!((Object)(object)collider == (Object)null)) { GameObject val = Projectile.FindHitObject(collider); if ((Object)(object)val == (Object)null) { val = ((Component)collider).gameObject; } if (!((Object)(object)val == (Object)(object)((Component)character).gameObject) && WoodcuttingTargetUtils.IsWoodcuttingTarget(val)) { return true; } } } return false; } } [HarmonyPatch(typeof(Character), "AddRootMotion")] public static class NextSwingLungeRootMotionPatch { [HarmonyPrefix] private static void Prefix(Character __instance, ref Vector3 vel) { Humanoid val = (Humanoid)(object)((__instance is Humanoid) ? __instance : null); if (val != null) { Attack currentAttack = val.m_currentAttack; if (currentAttack != null && NextSwingLungeState.TryGetAttackLungeMultiplier(currentAttack, out var lungeMultiplier) && !(lungeMultiplier >= 1f)) { vel.x *= lungeMultiplier; vel.z *= lungeMultiplier; } } } } [BepInPlugin("sighsorry.NoCapLumber", "NoCapLumber", "1.0.0")] public class NoCapLumberPlugin : BaseUnityPlugin { public enum Toggle { On = 1, Off = 0 } private class ConfigurationManagerAttributes { [UsedImplicitly] public int? Order = null; [UsedImplicitly] public bool? Browsable = null; [UsedImplicitly] public string? Category = null; [UsedImplicitly] public Action<ConfigEntryBase>? CustomDrawer = null; } private class AcceptableShortcuts : AcceptableValueBase { public AcceptableShortcuts() : base(typeof(KeyboardShortcut)) { } public override object Clamp(object value) { return value; } public override bool IsValid(object value) { return true; } public override string ToDescriptionString() { return "# Acceptable values: " + string.Join(", ", UnityInput.Current.SupportedKeyCodes); } } internal const string ModName = "NoCapLumber"; internal const string ModVersion = "1.0.0"; internal const string Author = "sighsorry"; private const string ModGUID = "sighsorry.NoCapLumber"; private static string ConfigFileName = "sighsorry.NoCapLumber.cfg"; private static string ConfigFileFullPath; internal static string ConnectionError; private static NoCapLumberPlugin? _instance; private readonly Harmony _harmony = new Harmony("sighsorry.NoCapLumber"); public static readonly ManualLogSource NoCapLumberLogger; private static readonly ConfigSync ConfigSync; private FileSystemWatcher? _watcher; private readonly object _reloadLock = new object(); private DateTime _lastConfigReloadTime; private DateTime _suppressWatcherUntil; private const long RELOAD_DELAY = 10000000L; private const long LOCAL_SAVE_SUPPRESS_DELAY = 15000000L; internal const string TreeHealthNumberUiOff = "Off"; internal const string TreeHealthNumberUiTreeHpCurrentMax = "Tree HP Current/Max"; internal const string TreeHealthNumberUiTreeCurrentMax = "Tree Current/Max"; internal const string TreeHealthNumberUiCurrentMax = "Current/Max"; private static ConfigEntry<Toggle> _serverConfigLocked; internal static ConfigEntry<Toggle> _enableAxeComboOnTrees; internal static ConfigEntry<float> _oneHandedAxeTreeChopDamageMultiplier; internal static ConfigEntry<float> _dualAxesTreeChopDamageMultiplier; internal static ConfigEntry<float> _battleAxeTreeChopDamageMultiplier; internal static ConfigEntry<Toggle> _blockAxeSlashOnStumpTargets; internal static ConfigEntry<Toggle> _enableNextSwingLungeSuppressionAfterTreeChop; internal static ConfigEntry<string> _treeHealthNumberUiMode; internal static ConfigEntry<Toggle> _treeHealthNumberUiDragLock; internal static ConfigEntry<Toggle> _treeHealthNumberUiSizeLock; internal static ConfigEntry<float> _treeHealthNumberUiPosX; internal static ConfigEntry<float> _treeHealthNumberUiPosY; internal static ConfigEntry<float> _treeHealthNumberUiFontSize; internal static ConfigEntry<Toggle> _showTreeHealthBarUi; internal static ConfigEntry<Toggle> _treeHealthBarUiDragLock; internal static ConfigEntry<Toggle> _treeHealthBarUiSizeLock; internal static ConfigEntry<float> _treeHealthBarUiPosX; internal static ConfigEntry<float> _treeHealthBarUiPosY; internal static ConfigEntry<float> _treeHealthBarUiWidth; internal static ConfigEntry<float> _treeHealthBarUiHeight; internal static ConfigEntry<string> _treeHealthBarHighHpColor; internal static ConfigEntry<string> _treeHealthBarMidHpColor; internal static ConfigEntry<string> _treeHealthBarLowHpColor; internal static ConfigEntry<float> _treeHealthUiDurationSeconds; public void Awake() { _instance = this; bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; _serverConfigLocked = config("1 - General", "Lock Configuration", Toggle.On, orderedDescription("If on, the configuration is locked and can be changed by server admins only.", 700)); ConfigSync.AddLockingConfigEntry<Toggle>(_serverConfigLocked); _oneHandedAxeTreeChopDamageMultiplier = config("1 - General", "One-Handed Axe Tree Chop Damage Multiplier", 0.85f, orderedDescription("Multiplier applied to one-handed axe chop damage (AnimationState.OneHanded) against TreeBase/TreeLog.", 690, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 2f))); _battleAxeTreeChopDamageMultiplier = config("1 - General", "Battle Axe Tree Chop Damage Multiplier", 0.7f, orderedDescription("Multiplier applied to battle-axe chop damage (AnimationState.TwoHandedAxe) against TreeBase/TreeLog.", 680, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 2f))); _dualAxesTreeChopDamageMultiplier = config("1 - General", "Dual Axes Tree Chop Damage Multiplier", 0.55f, orderedDescription("Multiplier applied to dual-axes chop damage (AnimationState.DualAxes) against TreeBase/TreeLog.", 670, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 2f))); _enableAxeComboOnTrees = config("1 - General", "Enable Axe Combo On Trees", Toggle.On, orderedDescription("If on, axe combo attacks won't reset to the first swing when hitting TreeBase/Tree targets.", 660)); _blockAxeSlashOnStumpTargets = config("1 - General", "Block Axe Slash On Stump Targets", Toggle.On, orderedDescription("If on, player axe hits against stump targets remove slash damage so damage follows chop-based behavior.", 650)); _enableNextSwingLungeSuppressionAfterTreeChop = config("1 - General", "Enable Next Swing Lunge Suppression After Tree Chop", Toggle.On, orderedDescription("If on, after a player deals chop damage to a woodcutting target with an axe, the next eligible combo swing has no forward lunge (server-synced).", 640)); _treeHealthUiDurationSeconds = config("2 - UI", "Tree Health UI Duration Seconds", 1.5f, orderedDescription("How long the tree health UI stays visible after a valid hit. Recommended range: 0.5 to 10.", 500), synchronizedSetting: false); _showTreeHealthBarUi = config("2 - UI", "Show Tree Health Bar UI", Toggle.On, orderedDescription("If on, shows tree health bar when you hit with chop damage.", 490), synchronizedSetting: false); _treeHealthBarHighHpColor = config("2 - UI", "Tree Health Bar Color High HP", "#3ACC57", orderedDescription("Color used when tree HP is at or above 66.7%. Supports #RRGGBB or #RRGGBBAA.", 480), synchronizedSetting: false); _treeHealthBarMidHpColor = config("2 - UI", "Tree Health Bar Color Mid HP", "#E6C12A", orderedDescription("Color used when tree HP is at or above 33.3% and below 66.7%. Supports #RRGGBB or #RRGGBBAA.", 470), synchronizedSetting: false); _treeHealthBarLowHpColor = config("2 - UI", "Tree Health Bar Color Low HP", "#D94A44", orderedDescription("Color used when tree HP is below 33.3%. Supports #RRGGBB or #RRGGBBAA.", 460), synchronizedSetting: false); _treeHealthBarUiHeight = config("2 - UI", "Tree Health Bar UI Height", 20f, orderedDescription("Height of the health bar in UI units. Recommended range: 5 to 50.", 450), synchronizedSetting: false); _treeHealthBarUiWidth = config("2 - UI", "Tree Health Bar UI Width", 320f, orderedDescription("Width of the health bar in UI units. Recommended range: 50 to 500.", 440), synchronizedSetting: false); _treeHealthBarUiSizeLock = config("2 - UI", "Tree Health Bar UI Size Lock", Toggle.Off, orderedDescription("If on, resizing health bar UI with mouse is disabled.", 430), synchronizedSetting: false); _treeHealthBarUiPosX = config("2 - UI", "Tree Health Bar UI Position X", 0.5f, orderedDescription("Horizontal position of the health bar in normalized screen space. Recommended range: 0 to 1.", 420), synchronizedSetting: false); _treeHealthBarUiPosY = config("2 - UI", "Tree Health Bar UI Position Y", 0.76f, orderedDescription("Vertical position of the health bar in normalized screen space. Recommended range: 0 to 1.", 410), synchronizedSetting: false); _treeHealthBarUiDragLock = config("2 - UI", "Tree Health Bar UI Drag Lock", Toggle.Off, orderedDescription("If on, dragging (moving position) of health bar UI with mouse is disabled.", 400), synchronizedSetting: false); _treeHealthNumberUiMode = config("2 - UI", "Show Tree Health Number UI", "Tree HP Current/Max", orderedDescription("Number UI mode.", 390, (AcceptableValueBase?)(object)new AcceptableValueList<string>(new string[4] { "Off", "Tree HP Current/Max", "Tree Current/Max", "Current/Max" })), synchronizedSetting: false); _treeHealthNumberUiFontSize = config("2 - UI", "Tree Health Number UI Font Size", 22f, orderedDescription("Font size of the number UI. Recommended range: 10 to 50.", 380), synchronizedSetting: false); _treeHealthNumberUiSizeLock = config("2 - UI", "Tree Health Number UI Size Lock", Toggle.Off, orderedDescription("If on, resizing number UI (font size) with mouse is disabled.", 370), synchronizedSetting: false); _treeHealthNumberUiPosX = config("2 - UI", "Tree Health Number UI Position X", 0.5f, orderedDescription("Horizontal position of the number UI in normalized screen space. Recommended range: 0 to 1.", 360), synchronizedSetting: false); _treeHealthNumberUiPosY = config("2 - UI", "Tree Health Number UI Position Y", 0.79f, orderedDescription("Vertical position of the number UI in normalized screen space. Recommended range: 0 to 1.", 350), synchronizedSetting: false); _treeHealthNumberUiDragLock = config("2 - UI", "Tree Health Number UI Drag Lock", Toggle.Off, orderedDescription("If on, dragging (moving position) of number UI with mouse is disabled.", 340), synchronizedSetting: false); Assembly executingAssembly = Assembly.GetExecutingAssembly(); _harmony.PatchAll(executingAssembly); SaveWithRespectToConfigSet(); if (saveOnConfigSet) { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } SetupWatcher(); } private void OnDestroy() { if (_instance == this) { _instance = null; } SaveWithRespectToConfigSet(); _watcher?.Dispose(); } internal static void SaveConfigToDisk() { try { _instance?.SaveWithRespectToConfigSet(); } catch (Exception ex) { NoCapLumberLogger.LogWarning((object)("Failed to save config: " + ex.Message)); } } private void SetupWatcher() { _watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName); _watcher.Changed += ReadConfigValues; _watcher.Created += ReadConfigValues; _watcher.Renamed += ReadConfigValues; _watcher.IncludeSubdirectories = true; _watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _watcher.EnableRaisingEvents = true; } private void ReadConfigValues(object sender, FileSystemEventArgs e) { DateTime utcNow = DateTime.UtcNow; if (utcNow < _suppressWatcherUntil) { return; } long num = utcNow.Ticks - _lastConfigReloadTime.Ticks; if (num < 10000000) { return; } lock (_reloadLock) { utcNow = DateTime.UtcNow; if (utcNow < _suppressWatcherUntil) { return; } if (!File.Exists(ConfigFileFullPath)) { NoCapLumberLogger.LogWarning((object)"Config file does not exist. Skipping reload."); return; } try { NoCapLumberLogger.LogDebug((object)"Reloading configuration..."); SaveWithRespectToConfigSet(reload: true, save: false); NoCapLumberLogger.LogInfo((object)"Configuration reload complete."); } catch (Exception ex) { NoCapLumberLogger.LogError((object)("Error reloading configuration: " + ex.Message)); } } _lastConfigReloadTime = utcNow; } private void SaveWithRespectToConfigSet(bool reload = false, bool save = true) { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { if (reload) { ((BaseUnityPlugin)this).Config.Reload(); } if (save) { MarkLocalConfigWrite(); ((BaseUnityPlugin)this).Config.Save(); } } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } private void MarkLocalConfigWrite() { _suppressWatcherUntil = DateTime.UtcNow.AddTicks(15000000L); } private ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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); SyncedConfigEntry<T> syncedConfigEntry = ConfigSync.AddConfigEntry<T>(val2); syncedConfigEntry.SynchronizedConfig = synchronizedSetting; return val2; } private ConfigEntry<T> config<T>(string group, string name, T value, string description, bool synchronizedSetting = true) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting); } private static ConfigDescription orderedDescription(string description, int order, AcceptableValueBase? acceptableValues = null) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown return new ConfigDescription(description, acceptableValues, new object[1] { new ConfigurationManagerAttributes { Order = order } }); } static NoCapLumberPlugin() { string configPath = Paths.ConfigPath; char directorySeparatorChar = Path.DirectorySeparatorChar; ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName; ConnectionError = ""; NoCapLumberLogger = Logger.CreateLogSource("NoCapLumber"); ConfigSync = new ConfigSync("sighsorry.NoCapLumber") { DisplayName = "NoCapLumber", CurrentVersion = "1.0.0", MinimumRequiredVersion = "1.0.0" }; _serverConfigLocked = null; _enableAxeComboOnTrees = null; _oneHandedAxeTreeChopDamageMultiplier = null; _dualAxesTreeChopDamageMultiplier = null; _battleAxeTreeChopDamageMultiplier = null; _blockAxeSlashOnStumpTargets = null; _enableNextSwingLungeSuppressionAfterTreeChop = null; _treeHealthNumberUiMode = null; _treeHealthNumberUiDragLock = null; _treeHealthNumberUiSizeLock = null; _treeHealthNumberUiPosX = null; _treeHealthNumberUiPosY = null; _treeHealthNumberUiFontSize = null; _showTreeHealthBarUi = null; _treeHealthBarUiDragLock = null; _treeHealthBarUiSizeLock = null; _treeHealthBarUiPosX = null; _treeHealthBarUiPosY = null; _treeHealthBarUiWidth = null; _treeHealthBarUiHeight = null; _treeHealthBarHighHpColor = null; _treeHealthBarMidHpColor = null; _treeHealthBarLowHpColor = null; _treeHealthUiDurationSeconds = null; } } public static class KeyboardExtensions { public static bool IsKeyDown(this KeyboardShortcut shortcut) { //IL_0003: 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) return (int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey) && ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey); } public static bool IsKeyHeld(this KeyboardShortcut shortcut) { //IL_0003: 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) return (int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKey(((KeyboardShortcut)(ref shortcut)).MainKey) && ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey); } } public static class ToggleExtensions { public static bool IsOn(this NoCapLumberPlugin.Toggle value) { return value == NoCapLumberPlugin.Toggle.On; } public static bool IsOff(this NoCapLumberPlugin.Toggle value) { return value == NoCapLumberPlugin.Toggle.Off; } } public static class TreeChopDamagePatch { [HarmonyPatch(typeof(TreeBase), "Damage")] private static class TreeBaseDamagePatch { [HarmonyPrefix] private static void Prefix(TreeBase __instance, HitData hit) { if ((Object)(object)__instance != (Object)null) { ApplyPlayerTreeChopMultiplier(hit, ((Component)__instance).gameObject); } } } [HarmonyPatch(typeof(TreeLog), "Damage")] private static class TreeLogDamagePatch { [HarmonyPrefix] private static void Prefix(TreeLog __instance, HitData hit) { if ((Object)(object)__instance != (Object)null) { ApplyPlayerTreeChopMultiplier(hit, ((Component)__instance).gameObject); } } } [HarmonyPatch(typeof(Destructible), "Damage")] private static class DestructibleDamagePatch { [HarmonyPrefix] private static void Prefix(Destructible __instance, HitData hit) { if (!((Object)(object)__instance == (Object)null)) { GameObject gameObject = ((Component)__instance).gameObject; if (WoodcuttingTargetUtils.IsWoodcuttingTarget(gameObject)) { ApplyPlayerTreeChopMultiplier(hit, gameObject); } } } } [HarmonyPatch(typeof(WearNTear), "Damage")] private static class WearNTearDamagePatch { [HarmonyPrefix] private static void Prefix(WearNTear __instance, HitData hit) { if (!((Object)(object)__instance == (Object)null)) { GameObject gameObject = ((Component)__instance).gameObject; if (WoodcuttingTargetUtils.IsWoodcuttingTarget(gameObject)) { ApplyPlayerTreeChopMultiplier(hit, gameObject); } } } } private static void ApplyPlayerTreeChopMultiplier(HitData hit, GameObject? target) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Invalid comparison between Unknown and I4 //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Invalid comparison between Unknown and I4 if (hit == null || ((Object)(object)target != (Object)null && !WoodcuttingTargetUtils.IsWoodcuttingTarget(target)) || hit.m_damage.m_chop <= 0f) { return; } Character attacker = hit.GetAttacker(); if (!(attacker is Player) && (int)hit.m_hitType != 2) { return; } ItemData val = null; Humanoid val2 = (Humanoid)(object)((attacker is Humanoid) ? attacker : null); if (val2 != null) { val = val2.GetCurrentWeapon(); } if (val?.m_shared != null && (int)val.m_shared.m_skillType == 7) { if ((Object)(object)target != (Object)null && NoCapLumberPlugin._blockAxeSlashOnStumpTargets.Value.IsOn() && WoodcuttingTargetUtils.IsLikelyStumpName(((Object)target).name) && hit.m_damage.m_slash > 0f) { hit.m_damage.m_slash = 0f; } if (NoCapLumberPlugin._enableNextSwingLungeSuppressionAfterTreeChop.Value.IsOn()) { NextSwingLungeState.MarkPending(attacker); } float effectiveChopMultiplier = GetEffectiveChopMultiplier(val); if (!Mathf.Approximately(effectiveChopMultiplier, 1f)) { hit.m_damage.m_chop *= effectiveChopMultiplier; } } } private static float GetEffectiveChopMultiplier(ItemData? weapon) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Invalid comparison between Unknown and I4 if (weapon?.m_shared == null || (int)weapon.m_shared.m_skillType != 7) { return 1f; } AnimationState animationState = weapon.m_shared.m_animationState; if (1 == 0) { } float result = (((int)animationState == 8) ? NoCapLumberPlugin._battleAxeTreeChopDamageMultiplier.Value : (((int)animationState != 15) ? NoCapLumberPlugin._oneHandedAxeTreeChopDamageMultiplier.Value : NoCapLumberPlugin._dualAxesTreeChopDamageMultiplier.Value)); if (1 == 0) { } return result; } } public static class TreeHealthUiPatch { [HarmonyPatch(typeof(TreeBase), "Damage")] private static class TreeBaseDamagePatch { [HarmonyPrefix] private static void Prefix(TreeBase __instance, HitData hit) { if (ShouldShow(hit) && !((Object)(object)__instance?.m_nview == (Object)null)) { TreeHealthOverlay.Show(__instance.m_nview, WoodcuttingTargetUtils.ScaleMineHealth(__instance.m_health)); } } } [HarmonyPatch(typeof(TreeLog), "Damage")] private static class TreeLogDamagePatch { [HarmonyPrefix] private static void Prefix(TreeLog __instance, HitData hit) { if (ShouldShow(hit) && !((Object)(object)__instance?.m_nview == (Object)null)) { TreeHealthOverlay.Show(__instance.m_nview, WoodcuttingTargetUtils.ScaleMineHealth(__instance.m_health)); } } } [HarmonyPatch(typeof(Destructible), "Damage")] private static class DestructibleDamagePatch { [HarmonyPrefix] private static void Prefix(Destructible __instance, HitData hit) { if (ShouldShow(hit) && !((Object)(object)__instance?.m_nview == (Object)null)) { GameObject gameObject = ((Component)__instance).gameObject; if (WoodcuttingTargetUtils.IsWoodcuttingTarget(gameObject)) { TreeHealthOverlay.Show(__instance.m_nview, WoodcuttingTargetUtils.ScaleMineHealth(__instance.m_health)); } } } } [HarmonyPatch(typeof(WearNTear), "Damage")] private static class WearNTearDamagePatch { [HarmonyPrefix] private static void Prefix(WearNTear __instance, HitData hit) { if (ShouldShow(hit) && !((Object)(object)__instance?.m_nview == (Object)null)) { GameObject gameObject = ((Component)__instance).gameObject; if (WoodcuttingTargetUtils.IsWoodcuttingTarget(gameObject)) { TreeHealthOverlay.Show(__instance.m_nview, WoodcuttingTargetUtils.ScalePieceHealth(__instance.m_health)); } } } } private static bool ShouldShow(HitData hit) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Invalid comparison between Unknown and I4 if (!IsNumberUiEnabled() && !NoCapLumberPlugin._showTreeHealthBarUi.Value.IsOn()) { return false; } if (hit == null || hit.m_damage.m_chop <= 0f || (Object)(object)Player.m_localPlayer == (Object)null) { return false; } Character attacker = hit.GetAttacker(); if ((Object)(object)attacker == (Object)(object)Player.m_localPlayer) { return true; } if (attacker is Player) { return false; } return (int)hit.m_hitType == 2; } private static bool IsNumberUiEnabled() { return !string.Equals(NoCapLumberPlugin._treeHealthNumberUiMode.Value, "Off", StringComparison.Ordinal); } } internal sealed class TreeHealthOverlay : MonoBehaviour { private enum DragTarget { None, Bar, BarResize, Text, TextResize } private enum BarResizeMode { None, Width, Height } private static TreeHealthOverlay? _instance; private const float TextHitMinWidth = 80f; private const float TextHitMinHeight = 28f; private const float TextHitPaddingX = 22f; private const float TextHitPaddingY = 12f; private ZNetView? _target; private float _maxHealth = 1f; private float _lastKnownHealth = 1f; private float _hideAt; private Canvas? _canvas; private RectTransform? _barRoot; private RectTransform? _barFillRect; private Image? _barBackgroundImage; private Image? _barFillImage; private GameObject? _barDragOutlineObject; private RectTransform? _textRoot; private TextMeshProUGUI? _text; private GameObject? _textDragOutlineObject; private bool _fontBound; private float _appliedBarX = -1f; private float _appliedBarY = -1f; private float _appliedBarWidth = -1f; private float _appliedBarHeight = -1f; private float _appliedTextX = -1f; private float _appliedTextY = -1f; private float _appliedTextSize = -1f; private string _appliedBarHighColorHex = string.Empty; private string _appliedBarMidColorHex = string.Empty; private string _appliedBarLowColorHex = string.Empty; private Color _barHighColor = new Color(0.23f, 0.8f, 0.34f, 0.95f); private Color _barMidColor = new Color(0.9f, 0.76f, 0.16f, 0.95f); private Color _barLowColor = new Color(0.85f, 0.29f, 0.27f, 0.95f); private DragTarget _dragTarget = DragTarget.None; private Vector2 _dragPointerOffsetPixels; private Vector2 _textResizeCenterScreen; private float _textResizeStartRadius; private float _textResizeStartFontSize; private float _lastDragSaveAt; private string _lastMeasuredText = string.Empty; private float _lastMeasuredFontSize = -1f; private TMP_FontAsset? _lastMeasuredFont; private FontStyles _lastMeasuredFontStyle = (FontStyles)0; private BarResizeMode _barResizeMode = BarResizeMode.None; private Vector2 _barResizeStartLocal; private float _barResizeStartWidth; private float _barResizeStartHeight; private float _barResizeEdgeSign = 1f; private const float DragSaveIntervalSeconds = 0.2f; private const float BarResizeVerticalEdgeGrabPixels = 22f; private const float BarResizeHorizontalEdgeGrabPixels = 6f; private const float TextResizeCornerThresholdPixels = 16f; private readonly Color _dragOutlineColor = new Color(1f, 1f, 1f, 0.9f); private readonly Vector2 _dragOutlineDistance = new Vector2(2f, 2f); private readonly float _dragOutlineThickness = 2f; private readonly Color _backgroundColor = new Color(0f, 0f, 0f, 0.65f); private static TreeHealthOverlay Instance { get { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown if ((Object)(object)_instance != (Object)null) { return _instance; } GameObject val = new GameObject("NoCapLumber.TreeHealthOverlay"); Object.DontDestroyOnLoad((Object)(object)val); _instance = val.AddComponent<TreeHealthOverlay>(); return _instance; } } public static void Show(ZNetView target, float maxHealth) { if (!((Object)(object)target == (Object)null)) { TreeHealthOverlay instance = Instance; instance.EnsureUi(); instance.BindHudFontIfAvailable(); instance._target = target; instance._maxHealth = Mathf.Max(1f, maxHealth); instance._lastKnownHealth = instance.ReadCurrentHealth(); instance._hideAt = Time.time + Mathf.Max(0.5f, NoCapLumberPlugin._treeHealthUiDurationSeconds.Value); } } private void Update() { //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_canvas == (Object)null) { return; } bool flag = NoCapLumberPlugin._showTreeHealthBarUi.Value.IsOn(); bool flag2 = !string.Equals(NoCapLumberPlugin._treeHealthNumberUiMode.Value, "Off", StringComparison.Ordinal); HandleDragInput(flag, flag2); if (((Object)(object)_target == (Object)null || Time.time > _hideAt) && _dragTarget == DragTarget.None) { SetElementsVisible(showBar: false, showNumber: false); UpdateDragOutlines(showBar: false, showNumber: false); return; } BindHudFontIfAvailable(); ApplyLayout(force: false); RefreshBarColors(force: false); float num = (_lastKnownHealth = (((Object)(object)_target != (Object)null) ? ReadCurrentHealth() : _lastKnownHealth)); float num2 = Mathf.Clamp01(num / _maxHealth); SetElementsVisible(flag, flag2); if (flag2 && (Object)(object)_text != (Object)null) { string healthText = GetHealthText(num, _maxHealth); bool flag3 = !string.Equals(((TMP_Text)_text).text, healthText, StringComparison.Ordinal); if (flag3) { ((TMP_Text)_text).text = healthText; } RefreshTextBounds(flag3); } if (flag && (Object)(object)_barRoot != (Object)null && (Object)(object)_barFillRect != (Object)null) { RectTransform? barFillRect = _barFillRect; Rect rect = _barRoot.rect; barFillRect.SetSizeWithCurrentAnchors((Axis)0, ((Rect)(ref rect)).width * num2); if ((Object)(object)_barFillImage != (Object)null) { ((Graphic)_barFillImage).color = GetBarFillColor(num2); } } UpdateDragOutlines(flag, flag2); } private float ReadCurrentHealth() { if ((Object)(object)_target == (Object)null || !_target.IsValid()) { return _lastKnownHealth; } ZDO zDO = _target.GetZDO(); if (zDO == null) { return _lastKnownHealth; } float @float = zDO.GetFloat(ZDOVars.s_health, _maxHealth); return Mathf.Clamp(@float, 0f, _maxHealth); } private void EnsureUi() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_007b: 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: Expected O, but got Unknown //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Expected O, but got Unknown //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Expected O, but got Unknown //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_canvas != (Object)null)) { GameObject val = new GameObject("NoCapLumber.TreeHealthCanvas"); val.transform.SetParent(((Component)this).transform, false); _canvas = val.AddComponent<Canvas>(); _canvas.renderMode = (RenderMode)0; _canvas.sortingOrder = 1200; CanvasScaler val2 = val.AddComponent<CanvasScaler>(); val2.uiScaleMode = (ScaleMode)1; val2.referenceResolution = new Vector2(1920f, 1080f); val2.screenMatchMode = (ScreenMatchMode)0; val2.matchWidthOrHeight = 0.5f; val.AddComponent<GraphicRaycaster>(); GameObject val3 = new GameObject("BarRoot"); val3.transform.SetParent(val.transform, false); _barRoot = val3.AddComponent<RectTransform>(); _barRoot.pivot = new Vector2(0.5f, 0.5f); _barRoot.anchoredPosition = Vector2.zero; _barBackgroundImage = val3.AddComponent<Image>(); ((Graphic)_barBackgroundImage).color = _backgroundColor; _barDragOutlineObject = CreateDragOutline(val3.transform, "BarDragOutline"); GameObject val4 = new GameObject("BarFill"); val4.transform.SetParent(val3.transform, false); _barFillRect = val4.AddComponent<RectTransform>(); _barFillRect.anchorMin = new Vector2(0f, 0f); _barFillRect.anchorMax = new Vector2(0f, 1f); _barFillRect.pivot = new Vector2(0f, 0.5f); _barFillRect.anchoredPosition = Vector2.zero; _barFillRect.sizeDelta = Vector2.zero; _barFillImage = val4.AddComponent<Image>(); ((Graphic)_barFillImage).color = _barHighColor; GameObject val5 = new GameObject("TextRoot"); val5.transform.SetParent(val.transform, false); _textRoot = val5.AddComponent<RectTransform>(); _textRoot.pivot = new Vector2(0.5f, 0.5f); _textRoot.sizeDelta = new Vector2(80f, 28f); _textRoot.anchoredPosition = Vector2.zero; _text = val5.AddComponent<TextMeshProUGUI>(); ((TMP_Text)_text).text = string.Empty; ((TMP_Text)_text).alignment = (TextAlignmentOptions)514; ((TMP_Text)_text).fontSize = 22f; ((Graphic)_text).color = Color.white; ((TMP_Text)_text).textWrappingMode = (TextWrappingModes)0; _textDragOutlineObject = CreateRectBorder(val5.transform, "TextDragOutline", _dragOutlineColor, _dragOutlineThickness); RefreshBarColors(force: true); ApplyLayout(force: true); BindHudFontIfAvailable(); RefreshTextBounds(force: true); SetElementsVisible(showBar: false, showNumber: false); } } private void BindHudFontIfAvailable() { //IL_00a2: 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) if (!((Object)(object)_text == (Object)null) && !_fontBound && !((Object)(object)Hud.instance == (Object)null)) { TMP_Text val = (((Object)(object)Hud.instance.m_healthText != (Object)null) ? Hud.instance.m_healthText : Hud.instance.m_staminaText); if (!((Object)(object)val == (Object)null) && !((Object)(object)val.font == (Object)null)) { ((TMP_Text)_text).font = val.font; ((TMP_Text)_text).fontSharedMaterial = val.fontSharedMaterial; ((TMP_Text)_text).fontStyle = val.fontStyle; ((Graphic)_text).color = ((Graphic)val).color; ((TMP_Text)_text).alignment = (TextAlignmentOptions)514; _fontBound = true; ResetTextMeasureCache(); } } } private GameObject CreateDragOutline(Transform parent, string objectName) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_001e: 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_0036: 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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(objectName); val.transform.SetParent(parent, false); RectTransform val2 = val.AddComponent<RectTransform>(); val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; val2.sizeDelta = Vector2.zero; val2.anchoredPosition = Vector2.zero; Image val3 = val.AddComponent<Image>(); ((Graphic)val3).color = Color.clear; ((Graphic)val3).raycastTarget = false; Outline val4 = val.AddComponent<Outline>(); ((Shadow)val4).effectColor = _dragOutlineColor; ((Shadow)val4).effectDistance = _dragOutlineDistance; ((Shadow)val4).useGraphicAlpha = false; val.SetActive(false); return val; } private GameObject CreateRectBorder(Transform parent, string objectName, Color color, float thickness) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_001e: 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_0036: 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_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: 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) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(objectName); val.transform.SetParent(parent, false); RectTransform val2 = val.AddComponent<RectTransform>(); val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; val2.offsetMin = Vector2.zero; val2.offsetMax = Vector2.zero; CreateBorderEdge(val.transform, "Top", color, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, thickness)); CreateBorderEdge(val.transform, "Bottom", color, new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, thickness)); CreateBorderEdge(val.transform, "Left", color, new Vector2(0f, 0f), new Vector2(0f, 1f), new Vector2(0f, 0.5f), new Vector2(thickness, 0f)); CreateBorderEdge(val.transform, "Right", color, new Vector2(1f, 0f), new Vector2(1f, 1f), new Vector2(1f, 0.5f), new Vector2(thickness, 0f)); val.SetActive(false); return val; } private static void CreateBorderEdge(Transform parent, string name, Color color, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 sizeDelta) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_001e: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0038: 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_0054: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetParent(parent, false); RectTransform val2 = val.AddComponent<RectTransform>(); val2.anchorMin = anchorMin; val2.anchorMax = anchorMax; val2.pivot = pivot; val2.anchoredPosition = Vector2.zero; val2.sizeDelta = sizeDelta; Image val3 = val.AddComponent<Image>(); ((Graphic)val3).color = color; ((Graphic)val3).raycastTarget = false; } private void RefreshTextBounds(bool force) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: 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) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_textRoot == (Object)null || (Object)(object)_text == (Object)null) { return; } if (!EnsureTextFontAssigned()) { if (force) { _textRoot.sizeDelta = new Vector2(80f, 28f); } return; } string text = (string.IsNullOrEmpty(((TMP_Text)_text).text) ? "Tree HP 000/000" : ((TMP_Text)_text).text); if (!force && string.Equals(text, _lastMeasuredText, StringComparison.Ordinal) && Mathf.Approximately(((TMP_Text)_text).fontSize, _lastMeasuredFontSize) && (Object)(object)((TMP_Text)_text).font == (Object)(object)_lastMeasuredFont && ((TMP_Text)_text).fontStyle == _lastMeasuredFontStyle) { return; } Vector2 preferredValues = ((TMP_Text)_text).GetPreferredValues(text); float num = Mathf.Clamp(preferredValues.x + 22f, 80f, 1400f); float num2 = Mathf.Clamp(preferredValues.y + 12f, 28f, 260f); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(num, num2); _lastMeasuredText = text; _lastMeasuredFontSize = ((TMP_Text)_text).fontSize; _lastMeasuredFont = ((TMP_Text)_text).font; _lastMeasuredFontStyle = ((TMP_Text)_text).fontStyle; if (!force) { Vector2 val2 = _textRoot.sizeDelta - val; if (((Vector2)(ref val2)).sqrMagnitude < 0.01f) { return; } } _textRoot.sizeDelta = val; } private bool EnsureTextFontAssigned() { if ((Object)(object)_text == (Object)null) { return false; } if ((Object)(object)((TMP_Text)_text).font != (Object)null) { return true; } TMP_FontAsset val = null; TMP_Text val2 = null; if ((Object)(object)Hud.instance != (Object)null) { val2 = (((Object)(object)Hud.instance.m_healthText != (Object)null) ? Hud.instance.m_healthText : Hud.instance.m_staminaText); if ((Object)(object)val2 != (Object)null && (Object)(object)val2.font != (Object)null) { val = val2.font; } } if ((Object)(object)val == (Object)null) { val = TMP_Settings.defaultFontAsset; } if ((Object)(object)val == (Object)null) { return false; } ((TMP_Text)_text).font = val; if ((Object)(object)val2 != (Object)null && (Object)(object)val2.fontSharedMaterial != (Object)null) { ((TMP_Text)_text).fontSharedMaterial = val2.fontSharedMaterial; } else if ((Object)(object)((TMP_Text)_text).fontSharedMaterial == (Object)null && (Object)(object)((TMP_Asset)val).material != (Object)null) { ((TMP_Text)_text).fontSharedMaterial = ((TMP_Asset)val).material; } ResetTextMeasureCache(); return true; } private void ResetTextMeasureCache() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) _lastMeasuredText = string.Empty; _lastMeasuredFontSize = -1f; _lastMeasuredFont = null; _lastMeasuredFontStyle = (FontStyles)0; } private void UpdateDragOutlines(bool showBar, bool showNumber) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_barRoot == (Object)null) && !((Object)(object)_textRoot == (Object)null)) { bool flag = IsBarDragLocked(); bool flag2 = IsBarSizeLocked(); bool flag3 = IsNumberDragLocked(); bool flag4 = IsNumberSizeLocked(); bool flag5 = !flag || !flag2; bool flag6 = !flag3 || !flag4; bool visible = Cursor.visible; Vector2 val = Vector2.op_Implicit(Input.mousePosition); bool flag7 = visible && showBar && flag5 && RectTransformUtility.RectangleContainsScreenPoint(_barRoot, val, (Camera)null); bool flag8 = visible && showNumber && flag6 && RectTransformUtility.RectangleContainsScreenPoint(_textRoot, val, (Camera)null); bool flag9 = showBar && (_dragTarget == DragTarget.Bar || _dragTarget == DragTarget.BarResize || (_dragTarget == DragTarget.None && flag7)); bool flag10 = showNumber && (_dragTarget == DragTarget.Text || _dragTarget == DragTarget.TextResize || (_dragTarget == DragTarget.None && flag8)); if ((Object)(object)_barDragOutlineObject != (Object)null && _barDragOutlineObject.activeSelf != flag9) { _barDragOutlineObject.SetActive(flag9); } if ((Object)(object)_textDragOutlineObject != (Object)null && _textDragOutlineObject.activeSelf != flag10) { _textDragOutlineObject.SetActive(flag10); } } } private void RefreshBarColors(bool force) { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: 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_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) string text = NormalizeColorConfig(NoCapLumberPlugin._treeHealthBarHighHpColor.Value); string text2 = NormalizeColorConfig(NoCapLumberPlugin._treeHealthBarMidHpColor.Value); string text3 = NormalizeColorConfig(NoCapLumberPlugin._treeHealthBarLowHpColor.Value); if (force || !string.Equals(text, _appliedBarHighColorHex, StringComparison.OrdinalIgnoreCase) || !string.Equals(text2, _appliedBarMidColorHex, StringComparison.OrdinalIgnoreCase) || !string.Equals(text3, _appliedBarLowColorHex, StringComparison.OrdinalIgnoreCase)) { _appliedBarHighColorHex = text; _appliedBarMidColorHex = text2; _appliedBarLowColorHex = text3; _barHighColor = ParseHtmlColor(text, new Color(0.23f, 0.8f, 0.34f, 0.95f)); _barMidColor = ParseHtmlColor(text2, new Color(0.9f, 0.76f, 0.16f, 0.95f)); _barLowColor = ParseHtmlColor(text3, new Color(0.85f, 0.29f, 0.27f, 0.95f)); } } private static string NormalizeColorConfig(string raw) { return string.IsNullOrWhiteSpace(raw) ? string.Empty : raw.Trim(); } private static Color ParseHtmlColor(string raw, Color fallback) { //IL_000c: 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_0046: 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_003f: 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) if (string.IsNullOrWhiteSpace(raw)) { return fallback; } string text = raw; if (!text.StartsWith("#", StringComparison.Ordinal)) { text = "#" + text; } Color val = default(Color); return ColorUtility.TryParseHtmlString(text, ref val) ? val : fallback; } private static string GetHealthText(float currentHealth, float maxHealth) { string value = NoCapLumberPlugin._treeHealthNumberUiMode.Value; string text = value; if (1 == 0) { } string result; if (string.Equals(text, "Tree Current/Max", StringComparison.Ordinal)) { result = $"Tree {currentHealth:0}/{maxHealth:0}"; } else { string a = text; result = ((!string.Equals(a, "Current/Max", StringComparison.Ordinal)) ? $"Tree HP {currentHealth:0}/{maxHealth:0}" : $"{currentHealth:0}/{maxHealth:0}"); } if (1 == 0) { } return result; } private Color GetBarFillColor(float ratio) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_0036: Unknown result type (might be due to invalid IL or missing references) if (ratio < 1f / 3f) { return _barLowColor; } if (ratio < 2f / 3f) { return _barMidColor; } return _barHighColor; } private void HandleDragInput(bool showBar, bool showNumber) { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_barRoot == (Object)null || (Object)(object)_textRoot == (Object)null) { return; } bool flag = IsBarDragLocked(); bool flag2 = IsBarSizeLocked(); bool flag3 = IsNumberDragLocked(); bool flag4 = IsNumberSizeLocked(); if (!Cursor.visible) { if (_dragTarget != 0) { _dragTarget = DragTarget.None; _barResizeMode = BarResizeMode.None; NoCapLumberPlugin.SaveConfigToDisk(); } return; } if (_dragTarget != 0 && IsDragOperationLocked(_dragTarget, flag, flag2, flag3, flag4)) { _dragTarget = DragTarget.None; _barResizeMode = BarResizeMode.None; NoCapLumberPlugin.SaveConfigToDisk(); return; } Vector2 val = Vector2.op_Implicit(Input.mousePosition); if (Input.GetMouseButtonDown(0)) { if (showNumber && !flag4 && IsOverTextResizeHandle(val)) { BeginTextResize(val); return; } if (showNumber && !flag3 && RectTransformUtility.RectangleContainsScreenPoint(_textRoot, val, (Camera)null)) { BeginDrag(DragTarget.Text, val, NoCapLumberPlugin._treeHealthNumberUiPosX.Value, NoCapLumberPlugin._treeHealthNumberUiPosY.Value); return; } if (showBar && !flag2 && IsOverBarResizeHandle(val)) { BeginBarResize(val); return; } if (showBar && !flag && RectTransformUtility.RectangleContainsScreenPoint(_barRoot, val, (Camera)null)) { BeginDrag(DragTarget.Bar, val, NoCapLumberPlugin._treeHealthBarUiPosX.Value, NoCapLumberPlugin._treeHealthBarUiPosY.Value); return; } } if (_dragTarget == DragTarget.None) { return; } if (!Input.GetMouseButton(0)) { _dragTarget = DragTarget.None; _barResizeMode = BarResizeMode.None; NoCapLumberPlugin.SaveConfigToDisk(); return; } if (_dragTarget == DragTarget.BarResize) { if (UpdateBarResize(val) && Time.unscaledTime - _lastDragSaveAt >= 0.2f) { _lastDragSaveAt = Time.unscaledTime; NoCapLumberPlugin.SaveConfigToDisk(); } return; } if (_dragTarget == DragTarget.TextResize) { if (UpdateTextResize(val) && Time.unscaledTime - _lastDragSaveAt >= 0.2f) { _lastDragSaveAt = Time.unscaledTime; NoCapLumberPlugin.SaveConfigToDisk(); } return; } float num = Mathf.Max(1f, (float)Screen.width); float num2 = Mathf.Max(1f, (float)Screen.height); Vector2 val2 = val + _dragPointerOffsetPixels; float value = Quantize01(val2.x / num); float value2 = Quantize01(val2.y / num2); DragTarget dragTarget = _dragTarget; if (1 == 0) { } bool flag5 = dragTarget switch { DragTarget.Bar => SetConfigFloat(NoCapLumberPlugin._treeHealthBarUiPosX, value) | SetConfigFloat(NoCapLumberPlugin._treeHealthBarUiPosY, value2), DragTarget.Text => SetConfigFloat(NoCapLumberPlugin._treeHealthNumberUiPosX, value) | SetConfigFloat(NoCapLumberPlugin._treeHealthNumberUiPosY, value2), _ => false, }; if (1 == 0) { } if (flag5 && Time.unscaledTime - _lastDragSaveAt >= 0.2f) { _lastDragSaveAt = Time.unscaledTime; NoCapLumberPlugin.SaveConfigToDisk(); } } private static bool IsNumberDragLocked() { return NoCapLumberPlugin._treeHealthNumberUiDragLock.Value.IsOn(); } private static bool IsNumberSizeLocked() { return NoCapLumberPlugin._treeHealthNumberUiSizeLock.Value.IsOn(); } private static bool IsBarDragLocked() { return NoCapLumberPlugin._treeHealthBarUiDragLock.Value.IsOn(); } private static bool IsBarSizeLocked() { return NoCapLumberPlugin._treeHealthBarUiSizeLock.Value.IsOn(); } private static bool IsDragOperationLocked(DragTarget dragTarget, bool barDragLocked, bool barSizeLocked, bool textDragLocked, bool textSizeLocked) { if (1 == 0) { } bool result = dragTarget switch { DragTarget.Bar => barDragLocked, DragTarget.BarResize => barSizeLocked, DragTarget.Text => textDragLocked, DragTarget.TextResize => textSizeLocked, _ => false, }; if (1 == 0) { } return result; } private bool IsOverTextResizeHandle(Vector2 mousePosition) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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_0064: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_textRoot == (Object)null) { return false; } if (!RectTransformUtility.RectangleContainsScreenPoint(_textRoot, mousePosition, (Camera)null)) { return false; } Vector2 val = default(Vector2); if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(_textRoot, mousePosition, (Camera)null, ref val)) { return false; } Rect rect = _textRoot.rect; float num = ((Rect)(ref rect)).width * 0.5f; float num2 = ((Rect)(ref rect)).height * 0.5f; float num3 = Mathf.Min(16f, num); float num4 = Mathf.Min(16f, num2); bool flag = Mathf.Abs(val.x) >= num - num3; bool flag2 = Mathf.Abs(val.y) >= num2 - num4; return flag && flag2; } private bool IsOverBarResizeHandle(Vector2 mousePosition) { //IL_0029: 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_004f: 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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) _barResizeMode = BarResizeMode.None; if ((Object)(object)_barRoot == (Object)null) { return false; } Vector2 val = default(Vector2); if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(_barRoot, mousePosition, (Camera)null, ref val)) { return false; } Rect rect = _barRoot.rect; float num = ((Rect)(ref rect)).width * 0.5f; float num2 = ((Rect)(ref rect)).height * 0.5f; float num3 = 22f; float num4 = 6f; if (Mathf.Abs(val.x) > num + num3 || Mathf.Abs(val.y) > num2 + num4) { return false; } float num5 = Mathf.Abs(Mathf.Abs(val.x) - num); float num6 = Mathf.Abs(Mathf.Abs(val.y) - num2); bool flag = num5 <= num3; bool flag2 = num6 <= num4; if (flag == flag2) { return false; } _barResizeMode = (flag ? BarResizeMode.Width : BarResizeMode.Height); return true; } private void BeginBarResize(Vector2 mousePosition) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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) Vector2 val = default(Vector2); if (!((Object)(object)_barRoot == (Object)null) && _barResizeMode != 0 && RectTransformUtility.ScreenPointToLocalPointInRectangle(_barRoot, mousePosition, (Camera)null, ref val)) { _barResizeStartLocal = val; _barResizeStartWidth = Mathf.Clamp(NoCapLumberPlugin._treeHealthBarUiWidth.Value, 50f, 500f); _barResizeStartHeight = Mathf.Clamp(NoCapLumberPlugin._treeHealthBarUiHeight.Value, 5f, 50f); _barResizeEdgeSign = ((_barResizeMode != BarResizeMode.Width) ? ((val.y >= 0f) ? 1f : (-1f)) : ((val.x >= 0f) ? 1f : (-1f))); _dragTarget = DragTarget.BarResize; } } private bool UpdateBarResize(Vector2 mousePosition) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_barRoot == (Object)null) { return false; } Vector2 val = default(Vector2); if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(_barRoot, mousePosition, (Camera)null, ref val)) { return false; } switch (_barResizeMode) { case BarResizeMode.Width: { float num3 = (val.x - _barResizeStartLocal.x) * _barResizeEdgeSign * 2f; float num4 = Mathf.Clamp(_barResizeStartWidth + num3, 50f, 500f); num4 = Mathf.Round(num4 * 10f) / 10f; return SetConfigFloatRange(NoCapLumberPlugin._treeHealthBarUiWidth, num4, 50f, 500f); } case BarResizeMode.Height: { float num = (val.y - _barResizeStartLocal.y) * _barResizeEdgeSign * 2f; float num2 = Mathf.Clamp(_barResizeStartHeight + num, 5f, 50f); num2 = Mathf.Round(num2 * 10f) / 10f; return SetConfigFloatRange(NoCapLumberPlugin._treeHealthBarUiHeight, num2, 5f, 50f); } default: return false; } } private void BeginTextResize(Vector2 mousePosition) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_textRoot == (Object)null)) { _dragTarget = DragTarget.TextResize; _textResizeCenterScreen = RectTransformUtility.WorldToScreenPoint((Camera)null, ((Transform)_textRoot).position); _textResizeStartRadius = Mathf.Max(20f, Vector2.Distance(mousePosition, _textResizeCenterScreen)); _textResizeStartFontSize = Mathf.Clamp(NoCapLumberPlugin._treeHealthNumberUiFontSize.Value, 10f, 50f); } } private bool UpdateTextResize(Vector2 mousePosition) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(1f, Vector2.Distance(mousePosition, _textResizeCenterScreen)); float num2 = num / _textResizeStartRadius; float num3 = Mathf.Clamp(_textResizeStartFontSize * num2, 10f, 50f); num3 = Mathf.Round(num3 * 10f) / 10f; return SetConfigFloatRange(NoCapLumberPlugin._treeHealthNumberUiFontSize, num3, 10f, 50f); } private void BeginDrag(DragTarget target, Vector2 mousePosition, float currentX, float currentY) { //IL_0043: 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_0045: 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) _dragTarget = target; float num = Mathf.Max(1f, (float)Screen.width); float num2 = Mathf.Max(1f, (float)Screen.height); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(Mathf.Clamp01(currentX) * num, Mathf.Clamp01(currentY) * num2); _dragPointerOffsetPixels = val - mousePosition; } private static bool SetConfigFloat(ConfigEntry<float> configEntry, float value) { float num = Mathf.Clamp01(value); if (Mathf.Abs(configEntry.Value - num) <= 0.0001f) { return false; } configEntry.Value = num; return true; } private static bool SetConfigFloatRange(ConfigEntry<float> configEntry, float value, float min, float max) { float num = Mathf.Clamp(value, min, max); if (Mathf.Abs(configEntry.Value - num) <= 0.0001f) { return false; } configEntry.Value = num; return true; } private static float Quantize01(float value) { float num = Mathf.Clamp01(value); return Mathf.Round(num * 10000f) / 10000f; } private void ApplyLayout(bool force) { //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_barRoot == (Object)null) && !((Object)(object)_textRoot == (Object)null) && !((Object)(object)_text == (Object)null)) { float num = Mathf.Clamp01(NoCapLumberPlugin._treeHealthBarUiPosX.Value); float num2 = Mathf.Clamp01(NoCapLumberPlugin._treeHealthBarUiPosY.Value); float num3 = Mathf.Clamp(NoCapLumberPlugin._treeHealthBarUiWidth.Value, 50f, 500f); float num4 = Mathf.Clamp(NoCapLumberPlugin._treeHealthBarUiHeight.Value, 5f, 50f); float num5 = Mathf.Clamp01(NoCapLumberPlugin._treeHealthNumberUiPosX.Value); float num6 = Mathf.Clamp01(NoCapLumberPlugin._treeHealthNumberUiPosY.Value); float num7 = Mathf.Clamp(NoCapLumberPlugin._treeHealthNumberUiFontSize.Value, 10f, 50f); if (force || !Mathf.Approximately(num, _appliedBarX) || !Mathf.Approximately(num2, _appliedBarY) || !Mathf.Approximately(num3, _appliedBarWidth) || !Mathf.Approximately(num4, _appliedBarHeight) || !Mathf.Approximately(num5, _appliedTextX) || !Mathf.Approximately(num6, _appliedTextY) || !Mathf.Approximately(num7, _appliedTextSize)) { _appliedBarX = num; _appliedBarY = num2; _appliedBarWidth = num3; _appliedBarHeight = num4; _appliedTextX = num5; _appliedTextY = num6; _appliedTextSize = num7; _barRoot.anchorMin = new Vector2(num, num2); _barRoot.anchorMax = new Vector2(num, num2); _barRoot.sizeDelta = new Vector2(num3, num4); _barRoot.anchoredPosition = Vector2.zero; _textRoot.anchorMin = new Vector2(num5, num6); _textRoot.anchorMax = new Vector2(num5, num6); _textRoot.anchoredPosition = Vector2.zero; ((TMP_Text)_text).fontSize = num7; } } } private void SetElementsVisible(bool showBar, bool showNumber) { if ((Object)(object)_barRoot != (Object)null && ((Component)_barRoot).gameObject.activeSelf != showBar) { ((Component)_barRoot).gameObject.SetActive(showBar); } if ((Object)(object)_textRoot != (Object)null && ((Component)_textRoot).gameObject.activeSelf != showNumber) { ((Component)_textRoot).gameObject.SetActive(showNumber); } } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] public static class RegisterAndCheckVersion { private static void Prefix(ZNetPeer peer, ref ZNet __instance) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown if (peer?.m_rpc != null) { if (__instance.IsServer()) { RpcHandlers.TrackPendingPeer(peer.m_rpc); } NoCapLumberPlugin.NoCapLumberLogger.LogDebug((object)"Registering version RPC handler"); peer.m_rpc.Register<ZPackage>("NoCapLumber_VersionCheck", (Action<ZRpc, ZPackage>)RpcHandlers.RPC_NoCapLumber_Version); NoCapLumberPlugin.NoCapLumberLogger.LogInfo((object)"Invoking version check"); ZPackage val = new ZPackage(); val.Write("1.0.0"); peer.m_rpc.Invoke("NoCapLumber_VersionCheck", new object[1] { val }); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] public static class VerifyClient { private static bool Prefix(ZRpc rpc, ZPackage pkg, ref ZNet __instance) { if (__instance.IsServer()) { RpcHandlers.TrackPendingPeer(rpc); } return true; } private static void Postfix(ZNet __instance) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown if (!__instance.IsServer() && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "NoCapLumberRequestAdminSync", new object[1] { (object)new ZPackage() }); } } } [HarmonyPatch(typeof(ZNet), "Update")] public static class DisconnectUnvalidatedPeers { private static void Postfix(ZNet __instance) { RpcHandlers.DisconnectTimedOutPeers(__instance); } } [HarmonyPatch(typeof(FejdStartup), "ShowConnectError")] public class ShowConnectionError { private static void Postfix(FejdStartup __instance) { if (__instance.m_connectionFailedPanel.activeSelf) { __instance.m_connectionFailedError.fontSizeMax = 25f; __instance.m_connectionFailedError.fontSizeMin = 15f; TMP_Text connectionFailedError = __instance.m_connectionFailedError; connectionFailedError.text = connectionFailedError.text + "\n" + NoCapLumberPlugin.ConnectionError; } } } [HarmonyPatch(typeof(ZNet), "Disconnect")] public static class RemoveDisconnectedPeerFromVerified { private static void Prefix(ZNetPeer peer, ref ZNet __instance) { if (__instance.IsServer() && peer?.m_rpc != null) { NoCapLumberPlugin.NoCapLumberLogger.LogInfo((object)("Peer (" + RpcHandlers.GetPeerName(peer.m_rpc) + ") disconnected, removing from validation state")); RpcHandlers.RemovePeer(peer.m_rpc); } } } public static class RpcHandlers { private static readonly HashSet<ZRpc> ValidatedPeers = new HashSet<ZRpc>(); private static readonly Dictionary<ZRpc, float> PendingValidation = new Dictionary<ZRpc, float>(); private static readonly object PeerStateLock = new object(); private const float ValidationTimeoutSeconds = 6f; public static string GetPeerName(ZRpc rpc) { try { object obj; if (rpc == null) { obj = null; } else { ISocket socket = rpc.m_socket; obj = ((socket != null) ? socket.GetHostName() : null); } if (obj == null) { obj = "Unknown"; } return (string)obj; } catch { return "Unknown"; } } public static void TrackPendingPeer(ZRpc rpc) { if (rpc == null) { return; } lock (PeerStateLock) { if (!ValidatedPeers.Contains(rpc)) { PendingValidation[rpc] = Time.unscaledTime + 6f; } } } public static void RemovePeer(ZRpc rpc) { if (rpc == null) { return; } lock (PeerStateLock) { PendingValidation.Remove(rpc); ValidatedPeers.Remove(rpc); } } public static void DisconnectTimedOutPeers(ZNet znet) { if ((Object)(object)znet == (Object)null || !znet.IsServer()) { return; } List<ZRpc> list = null; float unscaledTime = Time.unscaledTime; lock (PeerStateLock) { foreach (KeyValuePair<ZRpc, float> item in PendingValidation) { if (!(unscaledTime < item.Value)) { if (list == null) { list = new List<ZRpc>(); } list.Add(item.Key); } } if (list != null) { foreach (ZRpc item2 in list) { PendingValidation.Remove(item2); } } } if (list == null) { return; } foreach (ZRpc item3 in list) { NoCapLumberPlugin.NoCapLumberLogger.LogWarning((object)$"Peer ({GetPeerName(item3)}) failed version handshake within {6f:0.0}s, disconnecting"); item3.Invoke("Error", new object[1] { 3 }); } } private static void MarkPeerValidated(ZRpc rpc) { if (rpc == null) { return; } lock (PeerStateLock) { PendingValidation.Remove(rpc); ValidatedPeers.Add(rpc); } } public static void RPC_NoCapLumber_Version(ZRpc rpc, ZPackage pkg) { if (rpc == null || pkg == null) { return; } string text; try { text = pkg.ReadString(); } catch (Exception ex) { NoCapLumberPlugin.NoCapLumberLogger.LogWarning((object)("Version payload from peer (" + GetPeerName(rpc) + ") was invalid: " + ex.Message)); RemovePeer(rpc); if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { rpc.Invoke("Error", new object[1] { 3 }); } return; } NoCapLumberPlugin.NoCapLumberLogger.LogInfo((object)("Version check, local: 1.0.0, remote: " + text)); bool flag = (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer(); if (text != "1.0.0") { NoCapLumberPlugin.ConnectionError = "NoCapLumber Installed: 1.0.0\n Needed: " + text; if (flag) { RemovePeer(rpc); NoCapLumberPlugin.NoCapLumberLogger.LogWarning((object)("Peer (" + GetPeerName(rpc) + ") has incompatible version, disconnecting...")); rpc.Invoke("Error", new object[1] { 3 }); } } else if (!flag) { NoCapLumberPlugin.NoCapLumberLogger.LogInfo((object)"Received same version from server!"); } else { NoCapLumberPlugin.NoCapLumberLogger.LogInfo((object)("Adding peer (" + GetPeerName(rpc) + ") to validated list")); MarkPeerValidated(rpc); } } } internal static class WoodcuttingTargetUtils { private sealed class TargetClassification { public bool Initialized; public string CachedName = string.Empty; public bool IsWoodcuttingTarget; } private static readonly string[] StumpNameTokens = new string[3] { "stub", "stump", "stomp" }; private static readonly ConditionalWeakTable<GameObject, TargetClassification> TargetCache = new ConditionalWeakTable<GameObject, TargetClassification>(); public static bool IsWoodcuttingTarget(GameObject target) { if ((Object)(object)target == (Object)null) { return false; } TargetClassification orCreateValue = TargetCache.GetOrCreateValue(target); string text = ((Object)target).name ?? string.Empty; if (!orCreateValue.Initialized || !string.Equals(orCreateValue.CachedName, text, StringComparison.Ordinal)) { orCreateValue.CachedName = text; orCreateValue.IsWoodcuttingTarget = ComputeIsWoodcuttingTarget(target, text); orCreateValue.Initialized = true; } return orCreateValue.IsWoodcuttingTarget; } private static bool ComputeIsWoodcuttingTarget(GameObject target, string targetName) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Invalid comparison between Unknown and I4 if ((Object)(object)target.GetComponent<TreeBase>() != (Object)null || (Object)(object)target.GetComponent<TreeLog>() != (Object)null) { return true; } IDestructible component = target.GetComponent<IDestructible>(); if (component != null && (component.GetDestructibleType() & 2) > 0) { return true; } return IsLikelyStumpDestructible(target, targetName); } public static bool IsLikelyStumpName(string name) { if (string.IsNullOrWhiteSpace(name)) { return false; } string text = NormalizeName(name); string[] stumpNameTokens = StumpNameTokens; foreach (string value in stumpNameTokens) { if (text.IndexOf(value, StringComparison.Ordinal) >= 0) { return true; } } return false; } private static bool IsLikelyStumpDestructible(GameObject target, string targetName) { if ((Object)(object)target.GetComponent<Destructible>() == (Object)null) { return false; } if ((Object)(object)target.GetComponent<StaticPhysics>() == (Object)null) { return false; } return IsLikelyStumpName(targetName); } public static float ScaleMineHealth(float baseHealth) { if ((Object)(object)Game.instance == (Object)null) { return baseHealth; } return baseHealth + (float)Game.m_worldLevel * baseHealth * Game.instance.m_worldLevelMineHPMultiplier; } public static float ScalePieceHealth(float baseHealth) { if ((Object)(object)Game.instance == (Object)null) { return baseHealth; } return baseHealth + (float)Game.m_worldLevel * baseHealth * Game.instance.m_worldLevelPieceHPMultiplier; } private static string NormalizeName(string name) { string text = name.Trim(); int num = text.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase); if (num >= 0) { text = text.Substring(0, num); } return text.Trim().ToLowerInvariant(); } } } namespace ServerSync { [PublicAPI] internal abstract class OwnConfigEntryBase { public object? LocalBaseValue; public bool SynchronizedConfig = true; public abstract ConfigEntryBase BaseConfig { get; } } [PublicAPI] internal class SyncedConfigEntry<T> : OwnConfigEntryBase { public readonly ConfigEntry<T> SourceConfig; public override ConfigEntryBase BaseConfig => (ConfigEntryBase)(object)SourceConfig; public T Value { get { return SourceConfig.Value; } set { SourceConfig.Value = value; } } public SyncedConfigEntry(ConfigEntry<T> sourceConfig) { SourceConfig = sourceConfig; base..ctor(); } public void AssignLocalValue(T value) { if (LocalBaseValue == null) { Value = value; } else { LocalBaseValue = value; } } } internal abstract class CustomSyncedValueBase { public object? LocalBaseValue; public readonly string Identifier; public readonly Type Type; private object? boxedValue; protected bool localIsOwner; public readonly int Priority; public object? BoxedValue { get { return boxedValue; } set { boxedValue = value; this.ValueChanged?.Invoke(); } } public event Action? ValueChanged; protected CustomSyncedValueBase(ConfigSync configSync, string identifier, Type type, int priority) { Priority = priority; Identifier = identifier; Type = type; configSync.AddCustomValue(this); localIsOwner = configSync.IsSourceOfTruth; configSync.SourceOfTruthChanged += delegate(bool truth) { localIsOwner = truth; }; } } [PublicAPI] internal sealed class CustomSyncedValue<T> : CustomSyncedValueBase { public T Value { get { return (T)base.BoxedValue; } set { base.BoxedValue = value; } } public CustomSyncedValue(ConfigSync configSync, string identifier, T value = default(T), int priority = 0) : base(configSync, identifier, typeof(T), priority) { Value = value; } public void AssignLocalValue(T value) { if (localIsOwner) { Value = value; } else { LocalBaseValue = value; } } } internal class ConfigurationManagerAttributes { [UsedImplicitly] public bool? ReadOnly = false; } [PublicAPI] internal class ConfigSync { [HarmonyPatch(typeof(ZRpc), "HandlePackage")] private static class SnatchCurrentlyHandlingRPC { public static ZRpc? currentRpc; [HarmonyPrefix] private static void Prefix(ZRpc __instance) { currentRpc = __instance; } } [HarmonyPatch(typeof(ZNet), "Awake")] internal static class RegisterRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance) { isServer = __instance.IsServer(); foreach (ConfigSync configSync2 in configSyncs) { ZRoutedRpc.instance.Register<ZPackage>(configSync2.Name + " ConfigSync", (Action<long, ZPackage>)configSync2.RPC_FromOtherClientConfigSync); if (isServer) { configSync2.InitialSyncDone = true; Debug.Log((object)("Registered '" + configSync2.Name + " ConfigSync' RPC - waiting for incoming connections")); } } if (isServer) { ((MonoBehaviour)__instance).StartCoroutine(Wat