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 CraftMeOnce v1.0.2
CraftMeOnce.dll
Decompiled 4 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("CraftMeOnce")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("CraftMeOnce")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("7055ccb8-ebe8-49ab-ad51-d72bad2a5c42")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace CraftMeOnce; public static class Caching { [HarmonyPatch(typeof(Player), "OnSpawned")] public static class Player_OnSpawned { private static void Postfix(bool spawnValkyrie) { Logger.Log("Player_OnSpawned"); AddItemDrops(); } } public static readonly Dictionary<string, string> itemDropTranslatedKeys = new Dictionary<string, string>(); public static void AddItemDrops() { itemDropTranslatedKeys.Clear(); Recipe[] array = Resources.FindObjectsOfTypeAll<Recipe>(); foreach (Recipe val in array) { if ((Object)(object)val.m_item != (Object)null && val.m_item.m_itemData != null && val.m_item.m_itemData.m_shared != null) { string key = Localization.instance.Localize(val.m_item.m_itemData.m_shared.m_name); if (!itemDropTranslatedKeys.ContainsKey(key)) { itemDropTranslatedKeys.Add(key, val.m_item.m_itemData.m_shared.m_name); } } } } } [HarmonyPatch(typeof(Localization), "SetLanguage")] public class Localization_SetLanguage_Patch { private static void Postfix(string language) { Logger.Log("Language changed to: " + language); Caching.AddItemDrops(); } } internal class ConfigurationFile { public enum Toggle { Off, On } public static ConfigEntry<Toggle> modEnabled; public static ConfigEntry<Toggle> debug; public static ConfigEntry<Toggle> showExclamation; public static ConfigEntry<Vector2> btnPosition; public static ConfigEntry<Vector2> btnSize; public static ConfigEntry<string> characterForNotcraftedItems; public static ConfigEntry<Toggle> repairAll; public static ConfigEntry<string> repairAllItemsText; public static ConfigFile configFile; private static readonly string ConfigFileName = "Turbero.CraftMeOnce.cfg"; private static readonly string ConfigFileFullPath; internal static void LoadConfig(BaseUnityPlugin plugin) { //IL_0083: 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) configFile = plugin.Config; modEnabled = configFile.Bind<Toggle>("1 - General", "Mod Enabled", Toggle.On, "Enabling/Disabling this mod (default = On)"); debug = configFile.Bind<Toggle>("1 - General", "Debug Mode", Toggle.Off, "Enabling/Disabling the debugging in the console (default = Off)"); showExclamation = configFile.Bind<Toggle>("1 - General", "Show Exclamation", Toggle.On, "Turn on/off the exclamation mark in the names (default = On)"); btnPosition = configFile.Bind<Vector2>("2 - Config", "Button exclamation Position", new Vector2(-268f, 566f), "Left corner position for the map players list (default: x=-268, y=566)"); btnSize = configFile.Bind<Vector2>("2 - Config", "Button exclamation Size", new Vector2(29f, 29f), "Width/Height of the button exclamation in the workstations (default: x=29, y=29)"); characterForNotcraftedItems = configFile.Bind<string>("2 - Config", "Character for Not Crafted Items", "!", "Character to show the item has never been crafted (default = '!')"); repairAll = configFile.Bind<Toggle>("2 - Config", "Repair All", Toggle.On, "Enable/disable repairing all items in one click (default = true)"); repairAllItemsText = configFile.Bind<string>("2 - Config", "Repair All Text", "Repair all items", "Repair all text for repair button tooltip"); SetupWatcher(); } private static void SetupWatcher() { FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName); fileSystemWatcher.Changed += ReadConfigValues; fileSystemWatcher.Created += ReadConfigValues; fileSystemWatcher.Renamed += ReadConfigValues; fileSystemWatcher.IncludeSubdirectories = true; fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; fileSystemWatcher.EnableRaisingEvents = true; } private static void ReadConfigValues(object sender, FileSystemEventArgs e) { if (!File.Exists(ConfigFileFullPath)) { return; } try { Logger.Log("Attempting to reload configuration..."); configFile.Reload(); SettingsChanged(null, null); } catch { Logger.LogError("There was an issue loading " + ConfigFileName); } } private static void SettingsChanged(object sender, EventArgs e) { if ((Object)(object)BtnExclamationPatch.btnExclamation != (Object)null) { ((Component)BtnExclamationPatch.btnExclamation).gameObject.SetActive(modEnabled.Value == Toggle.On); } if (InventoryGui.IsVisible()) { MethodInfo method = ((object)InventoryGui.instance).GetType().GetMethod("SetupCrafting", BindingFlags.Instance | BindingFlags.NonPublic); if (method != null) { method.Invoke(InventoryGui.instance, new object[0]); } } } static ConfigurationFile() { string configPath = Paths.ConfigPath; char directorySeparatorChar = Path.DirectorySeparatorChar; ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName; } } [BepInPlugin("Turbero.CraftMeOnce", "Craft Me Once", "1.0.2")] public class CraftMeOnce : BaseUnityPlugin { public const string GUID = "Turbero.CraftMeOnce"; public const string NAME = "Craft Me Once"; public const string VERSION = "1.0.2"; private readonly Harmony harmony = new Harmony("Turbero.CraftMeOnce"); private void Awake() { ConfigurationFile.LoadConfig((BaseUnityPlugin)(object)this); harmony.PatchAll(); } private void onDestroy() { harmony.UnpatchSelf(); } } public class GameManager { private static readonly Dictionary<string, TMP_FontAsset> cachedFonts = new Dictionary<string, TMP_FontAsset>(); public static object GetPrivateValue(object obj, string name, BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic) { return obj.GetType().GetField(name, bindingAttr)?.GetValue(obj); } public static object GetPrivateMethod(object obj, string name, BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic, object[] args = null) { return obj.GetType().GetMethod(name, bindingAttr)?.Invoke(obj, args); } public static void CallPrivateMethod(object obj, string name, BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic, object[] args = null) { obj.GetType().GetMethod(name, bindingAttr)?.Invoke(obj, args); } public static TMP_FontAsset getFontAsset(string name) { if (!cachedFonts.ContainsKey(name)) { Logger.Log("Finding " + name + " font..."); TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>(); foreach (TMP_FontAsset val in array) { if (((Object)val).name == name) { Logger.Log(name + " font found."); cachedFonts.Add(name, val); return val; } } Logger.Log(name + " font NOT found."); return null; } return GeneralExtensions.GetValueSafe<string, TMP_FontAsset>(cachedFonts, name); } } public static class Logger { private static readonly ManualLogSource logger = Logger.CreateLogSource("Craft Me Once"); internal static void Log(object s) { if (ConfigurationFile.debug.Value != 0) { logger.LogInfo((object)s?.ToString()); } } internal static void LogInfo(object s) { logger.LogInfo((object)s?.ToString()); } internal static void LogWarning(object s) { string text = "Craft Me Once 1.0.2: " + ((s != null) ? s.ToString() : "null"); Debug.LogWarning((object)text); } internal static void LogError(object s) { string text = "Craft Me Once 1.0.2: " + ((s != null) ? s.ToString() : "null"); Debug.LogError((object)text); } } [HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList")] public static class UpdateCraftingPanelPatch { private static void Postfix(InventoryGui __instance, List<Recipe> recipes) { if (ConfigurationFile.modEnabled.Value == ConfigurationFile.Toggle.Off) { if ((Object)(object)BtnExclamationPatch.btnExclamation != (Object)null) { ((Component)BtnExclamationPatch.btnExclamation).gameObject.SetActive(false); } } else { if ((Object)(object)BtnExclamationPatch.btnExclamation == (Object)null || ConfigurationFile.showExclamation.Value == ConfigurationFile.Toggle.Off) { return; } ((Component)BtnExclamationPatch.btnExclamation).gameObject.SetActive(true); Logger.Log("UpdateCraftingPanelPatch - Postfix"); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } Transform val = ((Component)__instance).transform.Find("root/Crafting/RecipeList/Recipes/ListRoot"); int childCount = val.childCount; for (int i = 0; i < childCount; i++) { TextMeshProUGUI component = ((Component)val.GetChild(i).Find("name")).GetComponent<TextMeshProUGUI>(); if ((Object)(object)component == (Object)null) { continue; } Logger.Log("translatedText " + ((TMP_Text)component).text); if (findTranslatedKey(component, out var recipeKey)) { Logger.Log("found itemRecipeKeyValue: " + ((object)component)?.ToString() + " - " + recipeKey); if (!localPlayer.IsKnownMaterial(recipeKey)) { ((TMP_Text)component).text = "<color=yellow>" + ConfigurationFile.characterForNotcraftedItems.Value + "</color> " + Localization.instance.Localize(((TMP_Text)component).text); } else { ((TMP_Text)component).text = Localization.instance.Localize(((TMP_Text)component).text); } } } } } private static bool findTranslatedKey(TextMeshProUGUI translatedText, out string recipeKey) { if (Caching.itemDropTranslatedKeys.TryGetValue(((TMP_Text)translatedText).text, out recipeKey)) { return true; } string text = RemoveAmountSuffix(((TMP_Text)translatedText).text, " x"); Logger.Log("translated quantity check: " + text); if (Caching.itemDropTranslatedKeys.TryGetValue(text, out recipeKey)) { return true; } string text2 = CleanItemName(((TMP_Text)translatedText).text); Logger.Log("translated other mods check: " + text2); return Caching.itemDropTranslatedKeys.TryGetValue(text2, out recipeKey); } private static string RemoveAmountSuffix(string text, string indicator) { if (string.IsNullOrWhiteSpace(text)) { return text; } int num = text.LastIndexOf(indicator); if (num > 0 && num + 2 < text.Length) { string s = text.Substring(num + 2); if (int.TryParse(s, out var _)) { return text.Substring(0, num).Trim(); } } return text; } private static string CleanItemName(string raw) { if (string.IsNullOrEmpty(raw)) { return raw; } string input = raw; input = Regex.Replace(input, "<size=.*?</size>", "", RegexOptions.IgnoreCase); input = Regex.Replace(input, "<.*?>", "", RegexOptions.IgnoreCase); input = Regex.Replace(input, "\\s+#\\d+$", "", RegexOptions.IgnoreCase); return input.Trim(); } } [HarmonyPatch(typeof(InventoryGui), "SetupCrafting")] public static class BtnExclamationPatch { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static UnityAction <>9__3_0; internal void <Postfix>b__3_0() { ConfigurationFile.showExclamation.Value = ((ConfigurationFile.showExclamation.Value == ConfigurationFile.Toggle.Off) ? ConfigurationFile.Toggle.On : ConfigurationFile.Toggle.Off); } } private static GameObject btnExclamationGo; public static Button btnExclamation; private static TextMeshProUGUI buttonText; private static void Postfix(InventoryGui __instance) { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: 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_0111: Expected O, but got Unknown //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Expected O, but got Unknown if ((Object)(object)btnExclamationGo == (Object)null || (Object)(object)btnExclamation == (Object)null || (Object)(object)buttonText == (Object)null) { Transform val = ((Component)__instance.m_skillsDialog).transform.Find("SkillsFrame/Closebutton"); Transform transform = ((Component)__instance.m_crafting).transform; btnExclamationGo = Object.Instantiate<GameObject>(((Component)val).gameObject, transform); ((Object)btnExclamationGo).name = "BtnExclamation"; btnExclamationGo.transform.SetParent(transform, false); RectTransform component = btnExclamationGo.GetComponent<RectTransform>(); component.anchoredPosition = ConfigurationFile.btnPosition.Value; component.sizeDelta = ConfigurationFile.btnSize.Value; buttonText = btnExclamationGo.GetComponentInChildren<TextMeshProUGUI>(); ((TMP_Text)buttonText).font = GameManager.getFontAsset("Valheim-AveriaSerifLibre"); ((TMP_Text)buttonText).fontStyle = (FontStyles)0; ((TMP_Text)buttonText).alignment = (TextAlignmentOptions)514; btnExclamation = btnExclamationGo.GetComponent<Button>(); btnExclamation.onClick = new ButtonClickedEvent(); ButtonClickedEvent onClick = btnExclamation.onClick; object obj = <>c.<>9__3_0; if (obj == null) { UnityAction val2 = delegate { ConfigurationFile.showExclamation.Value = ((ConfigurationFile.showExclamation.Value == ConfigurationFile.Toggle.Off) ? ConfigurationFile.Toggle.On : ConfigurationFile.Toggle.Off); }; <>c.<>9__3_0 = val2; obj = (object)val2; } ((UnityEvent)onClick).AddListener((UnityAction)obj); } ((TMP_Text)buttonText).text = ConfigurationFile.characterForNotcraftedItems.Value; ((Graphic)buttonText).color = ((ConfigurationFile.showExclamation.Value == ConfigurationFile.Toggle.On) ? Color.yellow : Color.gray); } } [HarmonyPatch(typeof(InventoryGui), "Show")] public static class InventoryGuiRepairAllItemsText { [HarmonyPostfix] public static void Postfix(InventoryGui __instance) { ((Component)((Component)InventoryGui.instance).transform.Find("root/Crafting/RepairButton")).GetComponent<UITooltip>().m_text = ((ConfigurationFile.repairAll.Value == ConfigurationFile.Toggle.On) ? ConfigurationFile.repairAllItemsText.Value : "$inventory_repairbutton"); } } [HarmonyPatch(typeof(InventoryGui), "OnRepairPressed")] public static class InventoryGuiRepairAllItemsClick { [HarmonyPostfix] public static void Postfix(InventoryGui __instance) { //IL_0074: 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) if (ConfigurationFile.repairAll.Value == ConfigurationFile.Toggle.Off) { return; } CraftingStation currentCraftingStation = Player.m_localPlayer.GetCurrentCraftingStation(); if ((Object)(object)currentCraftingStation != (Object)null) { int num = 0; while ((bool)GameManager.GetPrivateMethod(__instance, "HaveRepairableItems")) { GameManager.CallPrivateMethod(__instance, "RepairOneItem"); num++; } if (num > 0) { currentCraftingStation.m_repairItemDoneEffects.Create(((Component)currentCraftingStation).transform.position, Quaternion.identity, (Transform)null, 1f, -1); } } } }