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 HammerCostCap v1.3.0
HammerCostCap.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("HammerCostCap")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+8e98c8e052d6fcb45fe4e8b236a4f39a4d16ef36")] [assembly: AssemblyProduct("HammerCostCap")] [assembly: AssemblyTitle("HammerCostCap")] [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 HammerCostCap { public enum CostAction { UpToDate, Recap, Capture } public sealed class CostState { public object Original { get; private set; } public object Capped { get; private set; } public CostAction Observe(object current) { if (current == null) { return CostAction.UpToDate; } if (Capped != null && current == Capped) { return CostAction.UpToDate; } if (Original != null && current == Original) { return CostAction.Recap; } return CostAction.Capture; } public void Capture(object original, object capped) { Original = original; Capped = capped; } } public enum CostMode { Full, Capped, Free } public static class CapLogic { public const int Cap = 1; public const CostMode DefaultMode = CostMode.Capped; public const float DeconstructWindow = 3f; public const string HintLabel = "Cost mode"; public static int CapAmount(CostMode mode, int amount) { switch (mode) { case CostMode.Free: return 0; case CostMode.Capped: if (amount <= 1) { return amount; } return 1; default: return amount; } } public static bool NeedsCap(CostMode mode, IReadOnlyCollection<int> amounts) { switch (mode) { case CostMode.Free: return amounts.Count > 0; case CostMode.Capped: foreach (int amount in amounts) { if (amount > 1) { return true; } } return false; default: return false; } } public static bool ShouldCap(CostMode mode, bool inHammer, bool excluded) { if (mode != CostMode.Full && inHammer) { return !excluded; } return false; } public static CostMode RefundMode(CostMode mode, bool localDeconstruct) { if (!localDeconstruct) { return CostMode.Full; } return mode; } public static bool IsLocalDeconstruct(bool insideRemoveCall, bool targetsRemovedPiece, float secondsSinceRemove) { if (!insideRemoveCall) { if (targetsRemovedPiece && secondsSinceRemove >= 0f) { return secondsSinceRemove <= 3f; } return false; } return true; } public static CostMode NextMode(CostMode mode) { return mode switch { CostMode.Capped => CostMode.Free, CostMode.Free => CostMode.Full, _ => CostMode.Capped, }; } public static string CycleMessage(CostMode mode) { return "Hammer cost: " + mode; } public static bool ShouldCycle(bool hammerInHand, bool inWorld, bool textInputOpen) { if (hammerInHand && inWorld) { return !textInputOpen; } return false; } public static CostMode ParseMode(string text) { if (!Enum.TryParse<CostMode>(text?.Trim(), ignoreCase: true, out var result)) { return CostMode.Capped; } return result; } public static HashSet<string> ParseExclusions(string text) { HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); if (string.IsNullOrEmpty(text)) { return hashSet; } string[] array = text.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length > 0) { hashSet.Add(text2); } } return hashSet; } public static string PrefabName(string objectName) { if (objectName == null) { return null; } int num = objectName.IndexOf("(Clone)", StringComparison.Ordinal); if (num >= 0) { return objectName.Substring(0, num); } return objectName; } } [BepInPlugin("lily.hammercostcap", "Hammer Cost Cap", "1.3.0")] public class HammerCostCapPlugin : BaseUnityPlugin { private static class Patches { [HarmonyPatch(typeof(PieceTable), "UpdateAvailable")] [HarmonyPrefix] private static void UpdateAvailable(PieceTable __instance) { Guarded("UpdateAvailable", delegate { if (!IsHammer(__instance)) { return; } _hammerTable = __instance; RefreshHammer(); foreach (GameObject piece in __instance.m_pieces) { Piece val = (((Object)(object)piece != (Object)null) ? piece.GetComponent<Piece>() : null); if ((Object)(object)val != (Object)null) { Apply(val, ((Object)piece).name); } } }); } [HarmonyPatch(typeof(Player), "HaveRequirements", new Type[] { typeof(Piece), typeof(RequirementMode) })] [HarmonyPrefix] private static void HaveRequirements(Piece piece) { Guarded("HaveRequirements", delegate { if ((Object)(object)piece != (Object)null) { Apply(piece, CapLogic.PrefabName(((Object)((Component)piece).gameObject).name)); } }); } [HarmonyPatch(typeof(Hud), "SetupPieceInfo")] [HarmonyPrefix] private static void SetupPieceInfo(Piece piece) { Guarded("SetupPieceInfo", delegate { if ((Object)(object)piece != (Object)null) { Apply(piece, CapLogic.PrefabName(((Object)((Component)piece).gameObject).name)); } }); } [HarmonyPatch(typeof(Piece), "DropResources")] [HarmonyPrefix] private static void DropResources(Piece __instance) { Guarded("DropResources", delegate { if ((Object)(object)__instance != (Object)null) { CapPlaced(__instance); } }); } [HarmonyPatch(typeof(Player), "RemovePiece")] [HarmonyPrefix] private static void RemovePiecePrefix(Player __instance) { Guarded("RemovePiece", delegate { NoteRemove(__instance); }); } [HarmonyPatch(typeof(Player), "RemovePiece")] [HarmonyPostfix] private static void RemovePiecePostfix() { _insideRemove = false; } [HarmonyPatch(typeof(WearNTear), "Remove")] [HarmonyPrefix] private static void WearNTearRemove(WearNTear __instance) { Guarded("WearNTear.Remove", delegate { if (_insideRemove) { NoteRemoved((Component)(object)__instance); } }); } } public const string Guid = "lily.hammercostcap"; public const string Version = "1.3.0"; private const string HammerItem = "Hammer"; private const string HammerTableName = "_HammerPieceTable"; private static HammerCostCapPlugin _instance; private static ConfigEntry<CostMode> _mode; private static ConfigEntry<string> _exclude; private static ConfigEntry<bool> _debugLog; private static ConfigEntry<KeyboardShortcut> _cycleKey; private static HashSet<string> _excluded = new HashSet<string>(StringComparer.Ordinal); private static readonly Dictionary<Piece, CostState> _states = new Dictionary<Piece, CostState>(); private static readonly HashSet<string> _warned = new HashSet<string>(); private static HashSet<string> _hammerNames = new HashSet<string>(StringComparer.Ordinal); private static PieceTable _hammerTable; private static int _hammerCount = -1; private Harmony _harmony; private static readonly MethodInfo _getRightItem = AccessTools.Method(typeof(Humanoid), "GetRightItem", (Type[])null, (Type[])null); private static GameObject _hint; private static TMP_Text _hintKey; private static bool _hintDirty = true; private static bool _hintGaveUp; private static bool _insideRemove; private static ZDOID _removedId = ZDOID.None; private static float _removedAt = -1000f; private void Awake() { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Expected O, but got Unknown _instance = this; _mode = ((BaseUnityPlugin)this).Config.Bind<CostMode>("General", "Mode", CostMode.Capped, "What every Hammer piece costs: Capped = at most 1 of each item it needs, Free = nothing, Full = the original cost. Applies at once (BEH-1-1, 3-1)."); _exclude = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Exclude", "", "Comma-separated prefab names that keep their original cost, e.g. portal_wood, piece_chest_wood (BEH-1-4)."); _cycleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "CycleKey", new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>()), "Cycles Mode (Capped -> Free -> Full) while the Hammer is equipped; written to this file (BEH-3-7)."); _cycleKey.SettingChanged += delegate { _hintDirty = true; }; _debugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLog", false, "Log each piece the first time it is capped, and every restore."); _excluded = CapLogic.ParseExclusions(_exclude.Value); _mode.SettingChanged += delegate { Reapply(); }; _exclude.SettingChanged += delegate { _excluded = CapLogic.ParseExclusions(_exclude.Value); Reapply(); }; try { _harmony = new Harmony("lily.hammercostcap"); _harmony.PatchAll(typeof(Patches)); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("HammerCostCap disabled: " + ex)); } } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } if ((Object)(object)_hint != (Object)null) { Object.Destroy((Object)(object)_hint); } } private void Update() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) try { KeyboardShortcut value = _cycleKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { CycleMode(); } UpdateHint(); } catch (Exception ex) { WarnOnce("update", "HammerCostCap hotkey failed: " + ex); } } private static bool HammerInHand() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || _getRightItem == null) { return false; } object? obj = _getRightItem.Invoke(localPlayer, null); ItemData val = (ItemData)((obj is ItemData) ? obj : null); if (val != null) { return IsHammer(val.m_shared?.m_buildPieces); } return false; } private static bool TextInputOpen() { if (!TextInput.IsVisible() && !Console.IsVisible()) { if ((Object)(object)Chat.instance != (Object)null) { return Chat.instance.HasFocus(); } return false; } return true; } private static void CycleMode() { if (CapLogic.ShouldCycle(HammerInHand(), (Object)(object)Player.m_localPlayer != (Object)null, TextInputOpen())) { _mode.Value = CapLogic.NextMode(_mode.Value); ((BaseUnityPlugin)_instance).Config.Save(); MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, CapLogic.CycleMessage(_mode.Value), 0, (Sprite)null, false, true); } } } private static void UpdateHint() { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0079: 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) KeyHints instance = KeyHints.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_buildHints == (Object)null) { _hint = null; return; } if ((Object)(object)_hint == (Object)null) { if (_hintGaveUp) { return; } if (!BuildHint(instance.m_buildHints)) { _hintGaveUp = true; return; } _hintDirty = true; } if (_hintDirty && (Object)(object)_hintKey != (Object)null) { TMP_Text hintKey = _hintKey; KeyboardShortcut value = _cycleKey.Value; hintKey.text = ((object)((KeyboardShortcut)(ref value)).MainKey/*cast due to .constrained prefix*/).ToString(); _hintDirty = false; } } private static bool BuildHint(GameObject bar) { Transform val = null; TMP_Text[] componentsInChildren = bar.GetComponentsInChildren<TMP_Text>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Transform val2 = HintRoot(componentsInChildren[i].transform, bar.transform); if ((Object)(object)val2 != (Object)null && ((Component)val2).GetComponentsInChildren<TMP_Text>(true).Length == 2) { val = val2; break; } } if ((Object)(object)val == (Object)null) { WarnOnce("hint", "build hint bar layout not recognised; the Cost mode hint is skipped. Tree: " + Tree(bar.transform, 0)); return false; } GameObject obj = Object.Instantiate<GameObject>(((Component)val).gameObject, val.parent); ((Object)obj).name = "HammerCostCapHint"; obj.transform.SetAsLastSibling(); TMP_Text[] componentsInChildren2 = obj.GetComponentsInChildren<TMP_Text>(true); TMP_Text obj2 = ((componentsInChildren2[0].text.Length <= componentsInChildren2[1].text.Length) ? componentsInChildren2[0] : componentsInChildren2[1]); ((obj2 == componentsInChildren2[0]) ? componentsInChildren2[1] : componentsInChildren2[0]).text = "Cost mode"; _hintKey = obj2; _hint = obj; Log("Cost mode hint added, cloned from " + ((Object)val).name); return true; } private static Transform HintRoot(Transform t, Transform bar) { while ((Object)(object)t != (Object)null && (Object)(object)t.parent != (Object)null && (Object)(object)t.parent != (Object)(object)bar) { if (t.parent.childCount >= 3) { return t; } t = t.parent; } if (!((Object)(object)t != (Object)null) || !((Object)(object)t.parent == (Object)(object)bar)) { return null; } return t; } private static string Tree(Transform t, int depth) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append('\n').Append(' ', depth * 2).Append(((Object)t).name); TMP_Text[] components = ((Component)t).GetComponents<TMP_Text>(); foreach (TMP_Text val in components) { stringBuilder.Append(" \"").Append(val.text).Append('"'); } foreach (Transform item in t) { Transform t2 = item; stringBuilder.Append(Tree(t2, depth + 1)); } return stringBuilder.ToString(); } private static void Reapply() { try { foreach (KeyValuePair<Piece, CostState> state in _states) { if ((Object)(object)state.Key != (Object)null) { Restore(state.Key); } } _states.Clear(); RefreshHammer(force: true); foreach (string hammerName in _hammerNames) { ZNetScene instance = ZNetScene.instance; GameObject obj = ((instance != null) ? instance.GetPrefab(hammerName) : null); Piece val = ((obj != null) ? obj.GetComponent<Piece>() : null); if ((Object)(object)val != (Object)null) { Apply(val, hammerName); } } Log("Mode " + _mode.Value.ToString() + ", " + _excluded.Count + " excluded; costs reapplied to " + _hammerNames.Count + " Hammer pieces"); } catch (Exception ex) { WarnOnce("reapply", "failed to reapply costs: " + ex); } } private static void RefreshHammer(bool force = false) { PieceTable val = _hammerTable; if ((Object)(object)val == (Object)null) { ObjectDB instance = ObjectDB.instance; object obj; if (instance == null) { obj = null; } else { GameObject itemPrefab = instance.GetItemPrefab("Hammer"); obj = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null); } val = ((ItemDrop)(obj?)).m_itemData?.m_shared?.m_buildPieces; if ((Object)(object)val == (Object)null) { return; } _hammerTable = val; } if (!force && val.m_pieces.Count == _hammerCount) { return; } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); foreach (GameObject piece in val.m_pieces) { if ((Object)(object)piece != (Object)null) { hashSet.Add(((Object)piece).name); } } _hammerNames = hashSet; _hammerCount = val.m_pieces.Count; } private static bool IsHammer(PieceTable table) { if ((Object)(object)table != (Object)null) { if (table != _hammerTable) { return ((Object)((Component)table).gameObject).name == "_HammerPieceTable"; } return true; } return false; } private static bool InHammer(string prefab) { if (_hammerNames.Count == 0) { RefreshHammer(); } return _hammerNames.Contains(prefab); } private static void Apply(Piece piece, string prefab) { if ((Object)(object)piece == (Object)null) { return; } if (!CapLogic.ShouldCap(_mode.Value, InHammer(prefab), _excluded.Contains(prefab))) { Restore(piece); return; } Requirement[] resources = piece.m_resources; if (resources == null) { return; } if (!_states.TryGetValue(piece, out var value)) { value = new CostState(); _states[piece] = value; } switch (value.Observe(resources)) { case CostAction.UpToDate: break; case CostAction.Recap: piece.m_resources = (Requirement[])value.Capped; break; case CostAction.Capture: { Requirement[] array = CapCopy(resources); value.Capture(resources, array); piece.m_resources = array; if (array != resources) { Log("capped " + prefab + ": " + Describe(resources) + " -> " + Describe(array)); } break; } } } private static void Restore(Piece piece) { if (_states.TryGetValue(piece, out var value)) { Requirement[] array = (Requirement[])value.Original; if (array != null && piece.m_resources != array && piece.m_resources == value.Capped) { piece.m_resources = array; Log("restored " + ((Object)((Component)piece).gameObject).name + ": " + Describe(array)); } } } private static Requirement[] CapCopy(Requirement[] reqs) { //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_0078: 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_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown CostMode value = _mode.Value; int[] array = new int[reqs.Length]; for (int i = 0; i < reqs.Length; i++) { array[i] = ((reqs[i] != null) ? reqs[i].m_amount : 0); } if (!CapLogic.NeedsCap(value, (IReadOnlyCollection<int>)(object)array)) { return reqs; } if (value == CostMode.Free) { return (Requirement[])(object)new Requirement[0]; } Requirement[] array2 = (Requirement[])(object)new Requirement[reqs.Length]; for (int j = 0; j < reqs.Length; j++) { Requirement val = reqs[j]; if (val != null) { array2[j] = new Requirement { m_resItem = val.m_resItem, m_amount = CapLogic.CapAmount(value, val.m_amount), m_extraAmountOnlyOneIngredient = val.m_extraAmountOnlyOneIngredient, m_amountPerLevel = val.m_amountPerLevel, m_recover = val.m_recover }; } } return array2; } private static void NoteRemove(Player player) { _insideRemove = true; NoteRemoved((Component)(object)player.GetHoveringPiece()); } private static void NoteRemoved(Component target) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) ZNetView val = (Object.op_Implicit((Object)(object)target) ? target.GetComponent<ZNetView>() : null); ZDOID val2 = (((Object)(object)val != (Object)null && val.GetZDO() != null) ? val.GetZDO().m_uid : ZDOID.None); if (val2 != ZDOID.None) { _removedId = val2; _removedAt = Time.time; } } private static void CapPlaced(Piece piece) { //IL_0035: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: 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_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: 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_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Expected O, but got Unknown string text = CapLogic.PrefabName(((Object)((Component)piece).gameObject).name); Requirement[] array = OriginalCost(text, piece); if (array == null) { return; } ZNetView component = ((Component)piece).GetComponent<ZNetView>(); ZDOID val = (((Object)(object)component != (Object)null && component.GetZDO() != null) ? component.GetZDO().m_uid : ZDOID.None); bool flag = CapLogic.IsLocalDeconstruct(_insideRemove, val != ZDOID.None && val == _removedId, Time.time - _removedAt); CostMode mode = CapLogic.RefundMode(_mode.Value, flag); if (!CapLogic.ShouldCap(mode, InHammer(text), _excluded.Contains(text))) { mode = CostMode.Full; } Requirement[] array2 = (Requirement[])(object)new Requirement[array.Length]; for (int i = 0; i < array.Length; i++) { Requirement val2 = array[i]; if (val2 != null) { array2[i] = new Requirement { m_resItem = val2.m_resItem, m_amount = CapLogic.CapAmount(mode, val2.m_amount), m_extraAmountOnlyOneIngredient = val2.m_extraAmountOnlyOneIngredient, m_amountPerLevel = val2.m_amountPerLevel, m_recover = val2.m_recover }; } } piece.m_resources = array2; Log("refund " + text + " as " + mode.ToString() + (flag ? " (own deconstruction)" : " (not ours)") + ": " + Describe(array2)); } private static Requirement[] OriginalCost(string prefab, Piece placed) { ZNetScene instance = ZNetScene.instance; object obj; if (instance == null) { obj = null; } else { GameObject prefab2 = instance.GetPrefab(prefab); obj = ((prefab2 != null) ? prefab2.GetComponent<Piece>() : null); } Piece val = (Piece)obj; if ((Object)(object)val == (Object)null) { return placed.m_resources; } if (_states.TryGetValue(val, out var value) && value.Original is Requirement[] array && (val.m_resources == value.Capped || val.m_resources == array)) { return array; } return val.m_resources; } private static string Describe(Requirement[] reqs) { StringBuilder stringBuilder = new StringBuilder("["); foreach (Requirement val in reqs) { if (stringBuilder.Length > 1) { stringBuilder.Append(", "); } stringBuilder.Append((val == null) ? "?" : (val.m_amount + " " + (Object.op_Implicit((Object)(object)val.m_resItem) ? ((Object)val.m_resItem).name : "?"))); } return stringBuilder.Append(']').ToString(); } private static void Log(string message) { if (_debugLog.Value) { ((BaseUnityPlugin)_instance).Logger.LogInfo((object)message); } } private static void WarnOnce(string key, string message) { if (_warned.Add(key)) { ((BaseUnityPlugin)_instance).Logger.LogWarning((object)message); } } private static void Guarded(string key, Action action) { try { action(); } catch (Exception ex) { WarnOnce(key, "HammerCostCap " + key + " failed: " + ex); } } } }