Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of HackAndSap v0.1.0
plugins/HackAndSap.dll
Decompiled 13 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Extensions; using Jotunn.Managers; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("HackAndSap")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+afdf9b0d3556fc85b43868f55bd7f2664ea710e9")] [assembly: AssemblyProduct("HackAndSap")] [assembly: AssemblyTitle("HackAndSap")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 HackAndSap { internal static class AxeUtil { private const float CloseInteractionDistance = 1.1f; public static ItemData GetEquippedAxe(Player player) { if ((Object)(object)player == (Object)null) { return null; } ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon(); if (!IsUsableAxe(currentWeapon)) { return null; } return currentWeapon; } public static bool IsUsableAxe(ItemData item) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 if (item?.m_shared == null) { return false; } if ((int)item.m_shared.m_skillType != 7) { return false; } if (item.m_shared.m_damages.m_chop <= 0f) { return false; } if (!item.HavePrimaryAttack()) { return false; } if (item.m_shared.m_useDurability) { return item.m_durability > 0f; } return true; } public static float GetInteractionReach() { return Math.Min(1.1f, Math.Max(0.5f, ModConfig.MaximumDistance.Value)); } } public sealed class CuttingInteraction : MonoBehaviour { private static readonly Collider[] NearbyColliders = (Collider[])(object)new Collider[64]; private TreeBase _tree; private TreeLog _log; private ZNetView _nview; public ZNetView NetView => _nview; private void Awake() { _tree = ((Component)this).GetComponent<TreeBase>(); _log = ((Component)this).GetComponent<TreeLog>(); _nview = ((Component)this).GetComponent<ZNetView>(); } public bool IsAvailable() { if (!ModConfig.Enabled.Value || (Object)(object)_nview == (Object)null || !_nview.IsValid()) { return false; } if ((Object)(object)_tree != (Object)null) { if (ModConfig.EnableTreeInteraction.Value) { return (Object)(object)_tree.m_logPrefab != (Object)null; } return false; } if ((Object)(object)_log != (Object)null) { return ModConfig.EnableLogInteraction.Value; } return false; } public bool IsColliderNearby(Player player, Collider collider) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)collider == (Object)null || !collider.enabled) { return false; } int num = Physics.OverlapSphereNonAlloc(((Character)player).GetCenterPoint(), AxeUtil.GetInteractionReach(), NearbyColliders, -1, (QueryTriggerInteraction)2); for (int i = 0; i < num; i++) { if ((Object)(object)NearbyColliders[i] == (Object)(object)collider) { return true; } } return false; } public bool TryGetCrosshairPoint(Player player, Collider collider, out Vector3 point) { //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_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) Vector3 val; if (!((Object)(object)collider != (Object)null)) { val = ((Component)this).transform.position; } else { Bounds bounds = collider.bounds; val = ((Bounds)(ref bounds)).center; } point = val; if ((Object)(object)player == (Object)null || (Object)(object)collider == (Object)null || !collider.enabled || (Object)(object)GameCamera.instance == (Object)null) { return false; } Transform transform = ((Component)GameCamera.instance).transform; RaycastHit val2 = default(RaycastHit); if (!collider.Raycast(new Ray(transform.position, transform.forward), ref val2, 50f)) { return false; } point = ((RaycastHit)(ref val2)).point; return true; } public string GetHoverName(Collider collider) { if (!IsAvailable() || !IsColliderNearby(Player.m_localPlayer, collider)) { return string.Empty; } if (!((Object)(object)_tree != (Object)null)) { return "Felled log"; } return "Tree"; } public string GetHoverText(Collider collider) { if (!IsAvailable() || !IsColliderNearby(Player.m_localPlayer, collider)) { return string.Empty; } Player localPlayer = Player.m_localPlayer; string text = (CuttingSession.IsTarget(this) ? "Stop hacking" : ((AxeUtil.GetEquippedAxe(localPlayer) != null) ? "Start hacking" : "Equip an axe")); return Localization.instance.Localize("[<color=yellow><b>$KEY_Use</b></color>] " + text); } public bool Interact(Humanoid user, bool hold, bool alt, Collider collider) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) if (hold || alt || !IsAvailable()) { return false; } Player val = (Player)(object)((user is Player) ? user : null); if (val == null || (Object)(object)val != (Object)(object)Player.m_localPlayer) { return false; } if (CuttingSession.IsTarget(this)) { CuttingSession.Cancel(notify: false); return false; } ItemData equippedAxe = AxeUtil.GetEquippedAxe(val); if (equippedAxe == null) { ((Character)val).Message((MessageType)2, "Equip an axe to start hacking", 0, (Sprite)null); return false; } if (!IsColliderNearby(val, collider)) { ((Character)val).Message((MessageType)2, "Move closer to start hacking", 0, (Sprite)null); return false; } TryGetCrosshairPoint(val, collider, out var point); CuttingSession.Begin(this, collider, point, val, equippedAxe); return false; } } internal static class CuttingSession { private static CuttingInteraction _target; private static Collider _collider; private static Vector3 _localSurfacePoint; private static Player _player; private static ItemData _axe; private static bool _armedForCancel; private static float _nextAttackAttempt; private static float _firstFailedAttempt = -1f; public static bool Active => (Object)(object)_target != (Object)null; public static bool IsTarget(CuttingInteraction target) { if (Active) { return (Object)(object)_target == (Object)(object)target; } return false; } public static void Begin(CuttingInteraction target, Collider collider, Vector3 surfacePoint, Player player, ItemData axe) { //IL_003f: 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_0045: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)target == (Object)null) && !((Object)(object)collider == (Object)null) && !((Object)(object)player == (Object)null) && axe != null) { if (Active) { Cancel(notify: false); } _target = target; _collider = collider; _localSurfacePoint = ((Component)collider).transform.InverseTransformPoint(surfacePoint); _player = player; _axe = axe; _armedForCancel = false; _nextAttackAttempt = 0f; _firstFailedAttempt = -1f; ModLog.Info("Started hacking '" + ((Object)((Component)target).gameObject).name + "' with '" + axe.m_shared.m_name + "'."); } } public static void Tick(float deltaTime) { if (!Active) { return; } if (!StillValid()) { Cancel(notify: false); return; } if (!_armedForCancel) { if (!ZInput.GetButton("Use")) { _armedForCancel = true; } } else if (ZInput.GetButtonDown("Use")) { Cancel(notify: false); return; } if (ZInput.GetButtonDown("Attack")) { Cancel(notify: false); } else { if (((Character)_player).InAttack() || Time.time < _nextAttackAttempt) { return; } FaceTarget(); bool num = ((Character)_player).StartAttack((Character)null, false); _nextAttackAttempt = Time.time + 0.12f; if (num) { _firstFailedAttempt = -1f; return; } if (_firstFailedAttempt < 0f) { _firstFailedAttempt = Time.time; } if (Time.time - _firstFailedAttempt > 1f) { ((Character)_player).Message((MessageType)2, "Unable to continue hacking", 0, (Sprite)null); Cancel(notify: false); } } } private static Vector3 GetSurfacePoint() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) return ((Component)_collider).transform.TransformPoint(_localSurfacePoint); } private static bool StillValid() { //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_target == (Object)null || (Object)(object)_collider == (Object)null || (Object)(object)_player == (Object)null || _axe == null) { return false; } if ((Object)(object)_player != (Object)(object)Player.m_localPlayer || ((Character)_player).IsDead()) { return false; } if (!_target.IsAvailable()) { return false; } if ((Object)(object)_target.NetView == (Object)null || !_target.NetView.IsValid()) { return false; } if (((Humanoid)_player).GetCurrentWeapon() != _axe || !AxeUtil.IsUsableAxe(_axe)) { return false; } if (((Character)_player).InDodge() || ((Character)_player).IsStaggering() || ((Character)_player).IsKnockedBack()) { return false; } Vector3 moveDir = ((Character)_player).GetMoveDir(); if (((Vector3)(ref moveDir)).sqrMagnitude > 0.01f) { return false; } return _target.IsColliderNearby(_player, _collider); } private static void FaceTarget() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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) Vector3 val = GetSurfacePoint() - ((Component)_player).transform.position; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude > 0.001f) { ((Character)_player).SetLookDir(((Vector3)(ref val)).normalized, 0.08f); } } public static void Cancel(bool notify) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) if (Active) { if (notify && (Object)(object)_player != (Object)null) { ((Character)_player).Message((MessageType)2, "Stopped hacking", 0, (Sprite)null); } ModLog.Info("Stopped hacking '" + ((Object)((Component)_target).gameObject).name + "'."); _target = null; _collider = null; _localSurfacePoint = Vector3.zero; _player = null; _axe = null; _armedForCancel = false; _firstFailedAttempt = -1f; } } } internal static class InteractionInstaller { public static void Install(GameObject root) { CuttingInteraction cuttingInteraction = root.GetComponent<CuttingInteraction>(); if ((Object)(object)cuttingInteraction == (Object)null) { cuttingInteraction = root.AddComponent<CuttingInteraction>(); } int num = 0; Collider[] componentsInChildren = root.GetComponentsInChildren<Collider>(true); foreach (Collider val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { TreeInteractionPoint treeInteractionPoint = ((Component)val).GetComponent<TreeInteractionPoint>(); if ((Object)(object)treeInteractionPoint == (Object)null) { treeInteractionPoint = ((Component)val).gameObject.AddComponent<TreeInteractionPoint>(); } treeInteractionPoint.Target = cuttingInteraction; num++; } } if (num == 0) { ModLog.Warn("No collider interaction points found on '" + ((Object)root).name + "'."); } } } [HarmonyPatch(typeof(TreeBase), "Awake")] internal static class TreeBaseAwakePatch { private static void Postfix(TreeBase __instance) { if ((Object)(object)__instance.m_logPrefab != (Object)null) { InteractionInstaller.Install(((Component)__instance).gameObject); } } } [HarmonyPatch(typeof(TreeLog), "Awake")] internal static class TreeLogAwakePatch { private static void Postfix(TreeLog __instance) { InteractionInstaller.Install(((Component)__instance).gameObject); if ((Object)(object)((Component)__instance).GetComponent<LogDecay>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<LogDecay>(); } } } public sealed class LogDecay : MonoBehaviour { private const string WorkedKey = "HackAndSap_LogLastWorked"; private ZNetView _nview; private void Awake() { _nview = ((Component)this).GetComponent<ZNetView>(); } private void Start() { EnsureTimestamp(); ((MonoBehaviour)this).InvokeRepeating("CheckDecay", Random.Range(15f, 45f), 60f); } private void EnsureTimestamp() { if (!((Object)(object)_nview == (Object)null) && _nview.IsValid() && _nview.IsOwner()) { ZDO zDO = _nview.GetZDO(); if (zDO != null && zDO.GetLong("HackAndSap_LogLastWorked", 0L) <= 0) { zDO.Set("HackAndSap_LogLastWorked", RenewableRegistry.NowTicks()); } } } public void MarkWorked() { if (!((Object)(object)_nview == (Object)null) && _nview.IsValid() && _nview.IsOwner()) { ZDO zDO = _nview.GetZDO(); if (zDO != null) { zDO.Set("HackAndSap_LogLastWorked", RenewableRegistry.NowTicks()); } } } private void CheckDecay() { //IL_0063: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.Enabled.Value || ModConfig.LogDecayDays.Value <= 0f || (Object)(object)_nview == (Object)null || !_nview.IsValid() || !_nview.IsOwner()) { return; } EnsureTimestamp(); ZDO zDO = _nview.GetZDO(); if (zDO == null) { return; } if (RenewableRegistry.IsBaseProtected(((Component)this).transform.position)) { zDO.Set("HackAndSap_LogLastWorked", RenewableRegistry.NowTicks()); return; } long num = zDO.GetLong("HackAndSap_LogLastWorked", 0L); if (num > 0 && !(Math.Max(0.0, (double)(RenewableRegistry.NowTicks() - num) / 10000000.0) < (double)ModConfig.LogDecayDays.Value * RenewableRegistry.GetDayLengthSeconds())) { ModLog.Info("Decayed abandoned log '" + ((Object)((Component)this).gameObject).name + "' away."); ZNetScene.instance.Destroy(((Component)this).gameObject); } } } internal static class ModConfig { public static ConfigEntry<bool> Enabled; public static ConfigEntry<bool> EnableTreeInteraction; public static ConfigEntry<bool> EnableLogInteraction; public static ConfigEntry<float> MaximumDistance; public static ConfigEntry<float> StumpRenewalDays; public static ConfigEntry<float> LogDecayDays; public static ConfigEntry<bool> ProtectPlayerBases; public static ConfigEntry<bool> EnableLogs; public static void Bind(ConfigFile config) { Enabled = ConfigFileExtensions.BindConfig<bool>(config, "General", "Enabled", true, "Master switch for Hack 'n Sap gameplay.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); EnableLogs = ConfigFileExtensions.BindConfig<bool>(config, "General", "Enable Logs", false, "Write detailed discovery and lifecycle messages to the BepInEx log. Client-only.", false, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); EnableTreeInteraction = ConfigFileExtensions.BindConfig<bool>(config, "Interaction", "Standing Trees", true, "Show the Get Hacking interaction on every discovered tree that produces a felled log.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); EnableLogInteraction = ConfigFileExtensions.BindConfig<bool>(config, "Interaction", "Felled Logs", true, "Show the Get Hacking interaction on felled logs and split logs.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); MaximumDistance = ConfigFileExtensions.BindConfig<float>(config, "Interaction", "Maximum Distance", 3.2f, "Optional upper cap for interaction distance. Hack 'n Sap always requires the player to stand directly beside the tree or log.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); StumpRenewalDays = ConfigFileExtensions.BindConfig<float>(config, "Renewal", "Stump Renewal Days", 5f, "In-game days before an unprotected stump becomes a natural sapling. Zero disables renewal.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); LogDecayDays = ConfigFileExtensions.BindConfig<float>(config, "Renewal", "Log Decay Days", 3f, "In-game days since a log was spawned or last chopped before it decays away without drops. Zero disables decay.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); ProtectPlayerBases = ConfigFileExtensions.BindConfig<bool>(config, "Renewal", "Protect Player Bases", true, "Prevent stump renewal and log decay inside a player-base effect area.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); } } internal static class ModLog { private static ManualLogSource _log; public static void Init(ManualLogSource log) { _log = log; } public static void Info(string message) { if (ModConfig.EnableLogs != null && ModConfig.EnableLogs.Value) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)message); } } } public static void Warn(string message) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)message); } } public static void Error(string message) { ManualLogSource log = _log; if (log != null) { log.LogError((object)message); } } } [BepInPlugin("com.virtualbjorn.hackandsap", "HackAndSap", "0.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.virtualbjorn.hackandsap"; public const string PluginName = "HackAndSap"; public const string PluginVersion = "0.1.0"; private Harmony _harmony; private void Awake() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown ModConfig.Bind(((BaseUnityPlugin)this).Config); ModLog.Init(((BaseUnityPlugin)this).Logger); _harmony = new Harmony("com.virtualbjorn.hackandsap"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Hack 'n Sap v0.1.0 loaded. Get Hacking."); } private void Update() { CuttingSession.Tick(Time.deltaTime); } private void OnDestroy() { CuttingSession.Cancel(notify: false); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal static class RenewableRegistry { private static readonly Dictionary<string, GameObject> SaplingsByTree = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, HashSet<string>> TreesByStump = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase); private static bool _initialized; public static void Initialize(ZNetScene scene) { if ((Object)(object)scene == (Object)null) { return; } SaplingsByTree.Clear(); TreesByStump.Clear(); List<GameObject> source = scene.m_prefabs.Where((GameObject p) => (Object)(object)p != (Object)null).ToList(); List<TreeBase> list = (from p in source select p.GetComponent<TreeBase>() into t where (Object)(object)t != (Object)null && (Object)(object)t.m_logPrefab != (Object)null select t).ToList(); List<Plant> source2 = (from p in source select p.GetComponent<Plant>() into p where (Object)(object)p != (Object)null select p).ToList(); Plant val = source2.FirstOrDefault(); foreach (TreeBase item in list) { string treeName = Normalize(((Object)((Component)item).gameObject).name); if ((Object)(object)item.m_stubPrefab != (Object)null) { RegisterStump(item.m_stubPrefab, treeName); } Plant val2 = ((IEnumerable<Plant>)source2).FirstOrDefault((Func<Plant, bool>)((Plant p) => p.m_grownPrefabs != null && p.m_grownPrefabs.Any((GameObject g) => (Object)(object)g != (Object)null && Normalize(((Object)g).name).Equals(treeName, StringComparison.OrdinalIgnoreCase)))); GameObject val3 = CreateNaturalSapling(((Component)item).gameObject, val2 ?? val); if ((Object)(object)val3 != (Object)null) { SaplingsByTree[treeName] = val3; } } _initialized = true; ModLog.Info($"Discovered {list.Count} fellable tree prefabs and " + $"{SaplingsByTree.Count} renewable saplings."); } private static void RegisterStump(GameObject stump, string treeName) { string key = Normalize(((Object)stump).name); if (!TreesByStump.TryGetValue(key, out var value)) { value = new HashSet<string>(StringComparer.OrdinalIgnoreCase); TreesByStump[key] = value; } value.Add(treeName); if ((Object)(object)stump.GetComponent<StumpRenewal>() == (Object)null) { stump.AddComponent<StumpRenewal>(); } } private static GameObject CreateNaturalSapling(GameObject matureTree, Plant basePlant) { if ((Object)(object)matureTree == (Object)null || (Object)(object)basePlant == (Object)null) { return null; } string value = Normalize(((Object)matureTree).name); string text = "HackAndSap_Sapling_" + StableId(value); PrefabManager instance = PrefabManager.Instance; GameObject val = instance.GetPrefab(text); bool flag = (Object)(object)val == (Object)null; if (flag) { val = instance.CreateClonedPrefab(text, ((Component)basePlant).gameObject); } if ((Object)(object)val == (Object)null) { return null; } ((Object)val).name = text; Plant component = val.GetComponent<Plant>(); if ((Object)(object)component == (Object)null) { return null; } component.m_name = "Young " + PrettyName(value); component.m_needCultivatedGround = false; component.m_destroyIfCantGrow = false; component.m_grownPrefabs = (GameObject[])(object)new GameObject[1] { matureTree }; if (flag) { instance.AddPrefab(val); } instance.RegisterToZNetScene(val); return val; } public static GameObject GetSapling(string treePrefab) { if (!_initialized || string.IsNullOrEmpty(treePrefab)) { return null; } SaplingsByTree.TryGetValue(Normalize(treePrefab), out var value); return value; } public static string InferUniqueTree(string stumpPrefab) { if (!_initialized || string.IsNullOrEmpty(stumpPrefab)) { return null; } if (!TreesByStump.TryGetValue(Normalize(stumpPrefab), out var value)) { return null; } if (value.Count != 1) { return null; } return value.First(); } public static string GetPrefabName(ZNetView nview, GameObject gameObject) { if ((Object)(object)nview != (Object)null && nview.IsValid()) { ZDO zDO = nview.GetZDO(); GameObject val = ((zDO != null && (Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(zDO.GetPrefab()) : null); if ((Object)(object)val != (Object)null) { return Normalize(((Object)val).name); } } if (!((Object)(object)gameObject != (Object)null)) { return null; } return Normalize(((Object)gameObject).name); } public static double GetDayLengthSeconds() { if (!((Object)(object)EnvMan.instance != (Object)null) || EnvMan.instance.m_dayLengthSec <= 0) { return 1800.0; } return EnvMan.instance.m_dayLengthSec; } public static long NowTicks() { if (!((Object)(object)ZNet.instance != (Object)null)) { return DateTime.UtcNow.Ticks; } return ZNet.instance.GetTime().Ticks; } public static bool IsBaseProtected(Vector3 point) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (ModConfig.ProtectPlayerBases.Value) { return (Object)(object)EffectArea.IsPointInsideArea(point, (Type)4, 0f) != (Object)null; } return false; } public static string Normalize(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } return value.Replace("(Clone)", string.Empty).Trim(); } private static string PrettyName(string value) { return value.Replace('_', ' '); } private static string StableId(string value) { uint num = 2166136261u; foreach (char c in value) { num = (num ^ char.ToLowerInvariant(c)) * 16777619; } return num.ToString("X8"); } } [HarmonyPatch(typeof(ZNetScene), "Awake")] [HarmonyPriority(0)] [HarmonyAfter(new string[] { "com.jotunn.jotunn" })] internal static class ZNetSceneAwakePatch { private static void Postfix(ZNetScene __instance) { RenewableRegistry.Initialize(__instance); } } [HarmonyPatch(typeof(TreeBase), "SpawnLog")] internal static class TreeBaseSpawnLogPatch { private static void Prefix(TreeBase __instance) { SpawnContext.Enter(__instance); } private static Exception Finalizer(Exception __exception) { SpawnContext.Exit(); return __exception; } } [HarmonyPatch(typeof(TreeLog), "RPC_Damage")] internal static class TreeLogDamagePatch { private static void Prefix(TreeLog __instance, out float __state) { __state = float.NaN; ZNetView val = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).GetComponent<ZNetView>() : null); if (!((Object)(object)val == (Object)null) && val.IsValid() && val.IsOwner()) { __state = val.GetZDO().GetFloat(ZDOVars.s_health, __instance.m_health); } } private static void Postfix(TreeLog __instance, HitData hit, float __state) { if (!float.IsNaN(__state) && hit != null && !(hit.m_damage.m_chop <= 0f)) { ZNetView val = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).GetComponent<ZNetView>() : null); if (!((Object)(object)val == (Object)null) && val.IsValid() && val.IsOwner() && val.GetZDO().GetFloat(ZDOVars.s_health, __instance.m_health) < __state) { ((Component)__instance).GetComponent<LogDecay>()?.MarkWorked(); } } } } internal static class SpawnContext { public static string CurrentTreePrefab { get; private set; } public static void Enter(TreeBase tree) { CurrentTreePrefab = RenewableRegistry.GetPrefabName(((Object)(object)tree != (Object)null) ? ((Component)tree).GetComponent<ZNetView>() : null, (tree != null) ? ((Component)tree).gameObject : null); } public static void Exit() { CurrentTreePrefab = null; } } public sealed class StumpRenewal : MonoBehaviour { private const string SourceKey = "HackAndSap_SourceTree"; private const string BornKey = "HackAndSap_StumpBorn"; private ZNetView _nview; private string _spawnSource; private void Awake() { _nview = ((Component)this).GetComponent<ZNetView>(); _spawnSource = SpawnContext.CurrentTreePrefab; } private void Start() { EnsureState(); ((MonoBehaviour)this).InvokeRepeating("CheckRenewal", Random.Range(15f, 45f), 60f); } private void EnsureState() { if ((Object)(object)_nview == (Object)null || !_nview.IsValid() || !_nview.IsOwner()) { return; } ZDO zDO = _nview.GetZDO(); if (zDO == null) { return; } if (string.IsNullOrEmpty(zDO.GetString("HackAndSap_SourceTree", string.Empty))) { string text = _spawnSource; if (string.IsNullOrEmpty(text)) { text = RenewableRegistry.InferUniqueTree(RenewableRegistry.GetPrefabName(_nview, ((Component)this).gameObject)); } if (!string.IsNullOrEmpty(text)) { zDO.Set("HackAndSap_SourceTree", text); } } if (zDO.GetLong("HackAndSap_StumpBorn", 0L) <= 0) { zDO.Set("HackAndSap_StumpBorn", RenewableRegistry.NowTicks()); } } private void CheckRenewal() { //IL_0063: 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_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.Enabled.Value || ModConfig.StumpRenewalDays.Value <= 0f || (Object)(object)_nview == (Object)null || !_nview.IsValid() || !_nview.IsOwner()) { return; } EnsureState(); ZDO zDO = _nview.GetZDO(); if (zDO == null) { return; } if (RenewableRegistry.IsBaseProtected(((Component)this).transform.position)) { zDO.Set("HackAndSap_StumpBorn", RenewableRegistry.NowTicks()); return; } long num = zDO.GetLong("HackAndSap_StumpBorn", 0L); if (num <= 0 || Math.Max(0.0, (double)(RenewableRegistry.NowTicks() - num) / 10000000.0) < (double)ModConfig.StumpRenewalDays.Value * RenewableRegistry.GetDayLengthSeconds()) { return; } string text = zDO.GetString("HackAndSap_SourceTree", string.Empty); GameObject sapling = RenewableRegistry.GetSapling(text); if ((Object)(object)sapling == (Object)null) { ModLog.Warn("No renewal sapling registered for stump source '" + text + "'."); return; } Quaternion val = Quaternion.Euler(0f, ((Component)this).transform.eulerAngles.y, 0f); GameObject val2 = Object.Instantiate<GameObject>(sapling, ((Component)this).transform.position, val); ZNetView val3 = (((Object)(object)val2 != (Object)null) ? val2.GetComponent<ZNetView>() : null); if ((Object)(object)val3 != (Object)null) { val3.SetLocalScale(((Component)this).transform.localScale); } ModLog.Info("Renewed stump '" + ((Object)((Component)this).gameObject).name + "' into sapling for '" + text + "'."); ZNetScene.instance.Destroy(((Component)this).gameObject); } } public sealed class TreeInteractionPoint : MonoBehaviour, Hoverable, Interactable { public CuttingInteraction Target; private Collider _collider; private Collider PointCollider { get { if (!((Object)(object)_collider != (Object)null)) { return _collider = ((Component)this).GetComponent<Collider>(); } return _collider; } } private void Awake() { _collider = ((Component)this).GetComponent<Collider>(); } public string GetHoverName() { if (!((Object)(object)Target != (Object)null)) { return string.Empty; } return Target.GetHoverName(PointCollider); } public string GetHoverText() { if (!((Object)(object)Target != (Object)null)) { return string.Empty; } return Target.GetHoverText(PointCollider); } public bool Interact(Humanoid user, bool hold, bool alt) { if ((Object)(object)Target != (Object)null) { return Target.Interact(user, hold, alt, PointCollider); } return false; } public bool UseItem(Humanoid user, ItemData item) { return false; } } }