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 WorkshopRepair v1.0.0
plugins/WorkshopRepair/WorkshopRepair.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Muindor.ServerConfig; using UnityEngine; using WorkshopRepair.Repair; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("Egor Fadeev")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("One click on the repair button mends every damaged item, and a crafting station also repairs what belongs to the other stations standing within 30 metres.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+b6da8f9d6aa103ba51cbd0a8ad70c07a2089e5f9")] [assembly: AssemblyProduct("Workshop Repair")] [assembly: AssemblyTitle("WorkshopRepair")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 Muindor.ServerConfig { public static class ConfigChannel { private static ConfigSync? _sync; private static ManualLogSource? _log; private static string _rpcName = string.Empty; public static void Activate(ConfigSync sync, ManualLogSource log) { _sync = sync; _log = log; _rpcName = sync.PluginGuid + ".ServerConfig"; sync.Changed += Broadcast; } internal static void OnNewConnection(ZNet net, ZNetPeer peer) { if (_sync != null && !net.IsServer() && peer != null && peer.m_server && peer.m_rpc != null) { _sync.Clear(); peer.m_rpc.Register<string>(_rpcName, (Action<ZRpc, string>)Receive); } } internal static void OnPeerInfo(ZNet net, ZRpc rpc) { if (_sync == null || !net.IsServer()) { return; } ZNetPeer peer = net.GetPeer(rpc); if (peer != null && peer.IsReady() && peer.m_rpc != null) { peer.m_rpc.Invoke(_rpcName, new object[1] { _sync.BuildPayload().Serialize() }); ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)("Sent the configuration to " + peer.m_playerName + " (ConfigPriority " + (_sync.Priority.Value ? "on" : "off") + ")")); } } } internal static void OnSessionEnd() { _sync?.Clear(); } internal static void PatchFailed(string patch, Exception exception) { ManualLogSource? log = _log; if (log != null) { log.LogError((object)$"{patch} failed, the configuration channel may be inactive: {exception}"); } } private static void Broadcast() { ZNet instance = ZNet.instance; if (_sync == null || (Object)(object)instance == (Object)null || !instance.IsServer()) { return; } string text = _sync.BuildPayload().Serialize(); int num = 0; foreach (ZNetPeer peer in instance.GetPeers()) { if (peer.IsReady() && peer.m_rpc != null) { peer.m_rpc.Invoke(_rpcName, new object[1] { text }); num++; } } if (num > 0) { ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)$"A setting changed; sent the configuration to {num} player(s)"); } } } private static void Receive(ZRpc rpc, string text) { if (_sync == null) { return; } ZNet instance = ZNet.instance; ConfigPayload payload; string error; if ((Object)(object)instance != (Object)null && instance.IsServer()) { ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)"Ignored a configuration sent to this server by a client"); } } else if (!ConfigPayload.TryParse(text, out payload, out error)) { ManualLogSource? log2 = _log; if (log2 != null) { log2.LogWarning((object)("Ignored the configuration from the server, keeping the local settings: " + error)); } } else { _sync.Apply(payload); } } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] internal static class ZNet_OnNewConnection_Patch { private static void Postfix(ZNet __instance, ZNetPeer peer) { try { ConfigChannel.OnNewConnection(__instance, peer); } catch (Exception exception) { ConfigChannel.PatchFailed("OnNewConnection", exception); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] internal static class ZNet_RPC_PeerInfo_Patch { private static void Postfix(ZNet __instance, ZRpc rpc) { try { ConfigChannel.OnPeerInfo(__instance, rpc); } catch (Exception exception) { ConfigChannel.PatchFailed("RPC_PeerInfo", exception); } } } [HarmonyPatch(typeof(ZNet), "OnDestroy")] internal static class ZNet_OnDestroy_Patch { private static void Postfix() { try { ConfigChannel.OnSessionEnd(); } catch (Exception exception) { ConfigChannel.PatchFailed("OnDestroy", exception); } } } public sealed class ConfigPayload { public const string Header = "muindor.config/1"; public string ModVersion { get; } public bool Priority { get; } public IList<KeyValuePair<string, string>> Values { get; } public ConfigPayload(string modVersion, bool priority, IEnumerable<KeyValuePair<string, string>>? values = null) { ModVersion = modVersion ?? string.Empty; Priority = priority; List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>(); if (values != null) { list.AddRange(values); } Values = list; } public string Serialize() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("muindor.config/1").Append('\n'); stringBuilder.Append(Escape(ModVersion, escapeEquals: false)).Append('\n'); stringBuilder.Append(Priority ? "true" : "false").Append('\n'); foreach (KeyValuePair<string, string> value in Values) { stringBuilder.Append(Escape(value.Key, escapeEquals: true)).Append('=').Append(Escape(value.Value ?? string.Empty, escapeEquals: false)) .Append('\n'); } return stringBuilder.ToString(); } public static bool TryParse(string? text, out ConfigPayload? payload, out string error) { payload = null; if (string.IsNullOrEmpty(text)) { error = "the payload is empty"; return false; } string[] array = text.Split(new char[1] { '\n' }); if (array.Length < 3) { error = "the payload is truncated"; return false; } if (Trim(array[0]) != "muindor.config/1") { error = "unknown format \"" + Trim(array[0]) + "\", expected \"muindor.config/1\""; return false; } string modVersion = Unescape(Trim(array[1])); string text2 = Trim(array[2]); if (text2 != "true" && text2 != "false") { error = "\"" + text2 + "\" is not a valid priority flag"; return false; } List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>(); for (int i = 3; i < array.Length; i++) { string text3 = Trim(array[i]); if (text3.Length != 0) { int num = IndexOfSeparator(text3); if (num < 0) { error = $"line {i + 1} has no key/value separator"; return false; } string text4 = Unescape(text3.Substring(0, num)); if (text4.Length == 0) { error = $"line {i + 1} has an empty key"; return false; } list.Add(new KeyValuePair<string, string>(text4, Unescape(text3.Substring(num + 1)))); } } payload = new ConfigPayload(modVersion, text2 == "true", list); error = string.Empty; return true; } private static string Trim(string line) { if (!line.EndsWith("\r", StringComparison.Ordinal)) { return line; } return line.Substring(0, line.Length - 1); } private static int IndexOfSeparator(string line) { for (int i = 0; i < line.Length; i++) { if (line[i] == '\\') { i++; } else if (line[i] == '=') { return i; } } return -1; } private static string Escape(string value, bool escapeEquals) { StringBuilder stringBuilder = new StringBuilder(value.Length); foreach (char c in value) { switch (c) { case '\\': stringBuilder.Append("\\\\"); continue; case '\n': stringBuilder.Append("\\n"); continue; case '\r': stringBuilder.Append("\\r"); continue; case '=': if (escapeEquals) { stringBuilder.Append("\\="); continue; } break; } stringBuilder.Append(c); } return stringBuilder.ToString(); } private static string Unescape(string value) { if (value.IndexOf('\\') < 0) { return value; } StringBuilder stringBuilder = new StringBuilder(value.Length); for (int i = 0; i < value.Length; i++) { if (value[i] != '\\' || i + 1 >= value.Length) { stringBuilder.Append(value[i]); continue; } char c = value[++i]; switch (c) { case 'n': stringBuilder.Append('\n'); break; case 'r': stringBuilder.Append('\r'); break; default: stringBuilder.Append(c); break; } } return stringBuilder.ToString(); } } public sealed class ConfigSync { private const string SyncedNote = "[synced] "; private readonly Dictionary<string, ISyncedEntry> _byKey = new Dictionary<string, ISyncedEntry>(StringComparer.Ordinal); private readonly List<ISyncedEntry> _entries = new List<ISyncedEntry>(); private readonly HashSet<ConfigEntryBase> _watched = new HashSet<ConfigEntryBase>(); private readonly ConfigFile _config; private readonly ManualLogSource _log; public string PluginGuid { get; } public string ModName { get; } public string ModVersion { get; } public ConfigEntry<bool> Priority { get; } public bool Locked { get; private set; } public string? ServerVersion { get; private set; } public int LockedCount { get; private set; } public event Action? Changed; public ConfigSync(ConfigFile config, string pluginGuid, string modName, string modVersion, ManualLogSource log) { _config = config; _log = log; PluginGuid = pluginGuid; ModName = modName; ModVersion = modVersion; Priority = config.Bind<bool>("Server", "ConfigPriority", false, "Server only, and off by default. Turn it on in the configuration of a dedicated server, or of the player hosting the session, to have every client that also has the mod run on the settings marked [synced] below instead of its own. The clients' own config files are never touched: their settings come back the moment they disconnect. This never makes the mod required on either side - players without it are unaffected, and a player with it can still join a server that does not have it."); _watched.Add((ConfigEntryBase)(object)Priority); config.SettingChanged += OnSettingChanged; } public SyncedEntry<T> Bind<T>(string section, string key, T defaultValue, string description) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown return Bind(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>())); } public SyncedEntry<T> Bind<T>(string section, string key, T defaultValue, ConfigDescription description) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown ConfigDescription val = new ConfigDescription("[synced] " + description.Description, description.AcceptableValues, description.Tags); ConfigEntry<T> val2 = _config.Bind<T>(section, key, defaultValue, val); SyncedEntry<T> syncedEntry = new SyncedEntry<T>(val2); _byKey.Add(syncedEntry.Key, syncedEntry); _entries.Add(syncedEntry); _watched.Add((ConfigEntryBase)(object)val2); return syncedEntry; } public ConfigPayload BuildPayload() { bool value = Priority.Value; List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>(value ? _entries.Count : 0); if (value) { foreach (ISyncedEntry entry in _entries) { try { list.Add(new KeyValuePair<string, string>(entry.Key, entry.LocalAsString())); } catch (Exception ex) { _log.LogWarning((object)(entry.Key + " could not be sent to the clients: " + ex.Message)); } } } return new ConfigPayload(ModVersion, value, list); } public void Apply(ConfigPayload payload) { ClearValues(); if (!payload.Priority) { Locked = false; ServerVersion = null; LockedCount = 0; _log.LogInfo((object)("The server runs " + ModName + " " + payload.ModVersion + " with ConfigPriority off; keeping the local settings")); return; } int num = 0; List<string> list = new List<string>(); foreach (KeyValuePair<string, string> value2 in payload.Values) { string error; if (!_byKey.TryGetValue(value2.Key, out ISyncedEntry value)) { list.Add(value2.Key); } else if (value.ApplyServerValue(value2.Value, out error)) { num++; } else { _log.LogWarning((object)("Keeping the local " + value2.Key + ": the server sent a value this version cannot read, " + error)); } } Locked = true; ServerVersion = payload.ModVersion; LockedCount = num; int num2 = _entries.Count - num; string text = ((list.Count > 0) ? string.Format(", {0} unknown here ({1})", list.Count, string.Join(", ", list.ToArray())) : string.Empty); string text2 = ((num2 > 0) ? $", {num2} kept local" : string.Empty); _log.LogInfo((object)$"The server ({ModName} {payload.ModVersion}) decides the settings: {num} applied{text2}{text}"); } public void Clear() { if (Locked || LockedCount != 0) { ClearValues(); Locked = false; ServerVersion = null; LockedCount = 0; _log.LogInfo((object)"Back to the local settings"); } } public string Describe() { if (!Locked) { return "local settings"; } return $"settings from the server ({ModName} {ServerVersion}, {LockedCount} values)"; } private void ClearValues() { foreach (ISyncedEntry entry in _entries) { entry.ClearServerValue(); } } private void OnSettingChanged(object sender, SettingChangedEventArgs args) { if (this.Changed != null) { ConfigEntryBase changedSetting = args.ChangedSetting; if (changedSetting != null && _watched.Contains(changedSetting)) { this.Changed(); } } } } public interface ISyncedEntry { string Key { get; } string LocalAsString(); bool ApplyServerValue(string text, out string error); void ClearServerValue(); } public sealed class SyncedEntry<T> : ISyncedEntry { private T _serverValue; private bool _hasServerValue; public ConfigEntry<T> Local { get; } public string Key { get; } public T Value { get { if (!_hasServerValue) { return Local.Value; } return _serverValue; } } public T LocalValue { get { return Local.Value; } set { Local.Value = value; } } public bool IsLocked => _hasServerValue; internal SyncedEntry(ConfigEntry<T> local) { Local = local; Key = ((ConfigEntryBase)local).Definition.Section + "." + ((ConfigEntryBase)local).Definition.Key; } public string LocalAsString() { return TomlTypeConverter.ConvertToString((object)Local.Value, typeof(T)); } public bool ApplyServerValue(string text, out string error) { object obj; try { obj = TomlTypeConverter.ConvertToValue(text, typeof(T)); } catch (Exception ex) { error = "\"" + text + "\" is not a valid " + typeof(T).Name + " (" + ex.Message + ")"; return false; } ConfigDescription description = ((ConfigEntryBase)Local).Description; AcceptableValueBase val = ((description != null) ? description.AcceptableValues : null); if (val != null && !val.IsValid(obj)) { obj = val.Clamp(obj); } _serverValue = (T)obj; _hasServerValue = true; error = string.Empty; return true; } public void ClearServerValue() { _hasServerValue = false; _serverValue = default(T); } public override string ToString() { T value = Value; return ((value != null) ? value.ToString() : null) ?? string.Empty; } } } namespace WorkshopRepair { public sealed class ModConfig { public ConfigSync Sync { get; } public SyncedEntry<bool> Enabled { get; } public ConfigEntry<bool> IsDebug { get; } public SyncedEntry<bool> RepairAllAtOnce { get; } public SyncedEntry<bool> UseNearbyStations { get; } public SyncedEntry<float> NearbyRadius { get; } public ModConfig(ConfigFile config) { //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown Sync = new ConfigSync(config, "muindor.WorkshopRepair", "Workshop Repair", "1.0.0", Plugin.Log); Enabled = Sync.Bind("General", "Enabled", defaultValue: true, "Turn the mod on. With this off the repair button works exactly as in vanilla."); IsDebug = config.Bind<bool>("General", "IsDebug", false, "Log every repaired item and the station that repaired it."); RepairAllAtOnce = Sync.Bind("Repair", "RepairAllAtOnce", defaultValue: true, "One click on the repair button repairs every item that can be repaired here. Off: one item per click, as in vanilla."); UseNearbyStations = Sync.Bind("Repair", "UseNearbyStations", defaultValue: true, "A crafting station also repairs the items that belong to another station standing nearby: at the workbench, a forge within reach repairs the bronze and iron gear too. That station must be usable as if you walked up to it - upgraded enough, under a roof, with its fire lit."); NearbyRadius = Sync.Bind("Repair", "NearbyRadius", 30f, new ConfigDescription("How far from you, in metres, another station may stand and still repair your items.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 100f), Array.Empty<object>())); } public string Describe() { string text = (UseNearbyStations.Value ? ("stations within " + NearbyRadius.Value.ToString("0.#", CultureInfo.InvariantCulture) + " m help") : "only the station in use repairs"); return (Enabled.Value ? "enabled" : "disabled") + ", " + (RepairAllAtOnce.Value ? "all items per click" : "one item per click") + ", " + text + ", " + Sync.Describe(); } } [BepInPlugin("muindor.WorkshopRepair", "Workshop Repair", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { private Harmony? _harmony; public static ManualLogSource Log { get; private set; } public static ModConfig Settings { get; private set; } public static bool Enabled { get { if (Settings != null) { return Settings.Enabled.Value; } return false; } } private void Awake() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Settings = new ModConfig(((BaseUnityPlugin)this).Config); ConfigChannel.Activate(Settings.Sync, Log); _harmony = new Harmony("muindor.WorkshopRepair"); _harmony.PatchAll(typeof(Plugin).Assembly); Log.LogInfo((object)("Workshop Repair 1.0.0 loaded: " + Settings.Describe())); } private void OnDestroy() { Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } public static void Debug(string message) { if (Settings != null && Settings.IsDebug.Value) { Log.LogInfo((object)message); } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "muindor.WorkshopRepair"; public const string PLUGIN_NAME = "Workshop Repair"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace WorkshopRepair.Repair { internal static class NearbyStations { private static readonly Dictionary<CraftingStation, bool> UsableThisFrame = new Dictionary<CraftingStation, bool>(); private static int _usableFrame = -1; public static CraftingStation? Find(Player player, CraftingStation current, Recipe recipe) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) string text = (((Object)(object)recipe.m_craftingStation != (Object)null) ? recipe.m_craftingStation.m_name : null); string text2 = (((Object)(object)recipe.m_repairStation != (Object)null) ? recipe.m_repairStation.m_name : null); if (text == null && text2 == null) { return null; } Vector3 position = ((Component)player).transform.position; float value = Plugin.Settings.NearbyRadius.Value; CraftingStation result = null; float num = float.MaxValue; foreach (CraftingStation allStation in CraftingStation.m_allStations) { if (!((Object)(object)allStation == (Object)null) && !((Object)(object)allStation == (Object)(object)current) && allStation.m_canRepair) { float num2 = Vector3.Distance(position, ((Component)allStation).transform.position); if (!(num2 > value) && !(num2 >= num) && RepairRules.StationRepairs(allStation.m_name, allStation.GetLevel(true), text, text2, recipe.m_minStationLevel) && IsUsable(allStation, player)) { result = allStation; num = num2; } } } return result; } private static bool IsUsable(CraftingStation station, Player player) { if (_usableFrame != Time.frameCount) { _usableFrame = Time.frameCount; UsableThisFrame.Clear(); } if (!UsableThisFrame.TryGetValue(station, out var value)) { value = station.CheckUsable(player, false); UsableThisFrame[station] = value; } return value; } } public static class RepairRules { public const int MaxCountedLevel = 4; public static bool StationRepairs(string stationName, int stationLevel, string? craftingStation, string? repairStation, int minStationLevel) { if (string.IsNullOrEmpty(stationName)) { return false; } if ((repairStation != null && repairStation == stationName) || (craftingStation != null && craftingStation == stationName)) { return CountedLevel(stationLevel) >= minStationLevel; } return false; } public static int CountedLevel(int stationLevel) { if (stationLevel >= 4) { return 4; } return stationLevel; } public static string SeveralRepairedMessage(int count) { return $"Repaired {count} items"; } } } namespace WorkshopRepair.Patches { [HarmonyPatch(typeof(InventoryGui))] internal static class RepairPatches { private static readonly List<ItemData> WornItems = new List<ItemData>(); private static readonly List<CraftingStation> UsedStations = new List<CraftingStation>(); private static CraftingStation? _lastNearbyStation; [HarmonyPostfix] [HarmonyPatch("CanRepair")] private static void CanRepairPostfix(ItemData item, ref bool __result) { _lastNearbyStation = null; if (!__result && Plugin.Enabled && Plugin.Settings.UseNearbyStations.Value) { _lastNearbyStation = FindNearbyStation(item); __result = (Object)(object)_lastNearbyStation != (Object)null; } } [HarmonyPrefix] [HarmonyPatch("RepairOneItem")] private static bool RepairOneItemPrefix(InventoryGui __instance) { if (!Plugin.Enabled || !Plugin.Settings.RepairAllAtOnce.Value) { return true; } RepairAll(__instance); return false; } private static CraftingStation? FindNearbyStation(ItemData item) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || item == null || !item.m_shared.m_canBeReparied) { return null; } CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation(); if ((Object)(object)currentCraftingStation == (Object)null || !currentCraftingStation.m_canRepair) { return null; } Recipe val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetRecipe(item) : null); if (!((Object)(object)val != (Object)null)) { return null; } return NearbyStations.Find(localPlayer, currentCraftingStation, val); } private static void RepairAll(InventoryGui gui) { //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0176: 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_018a: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation(); if (((Object)(object)currentCraftingStation == (Object)null && !localPlayer.NoCostCheat()) || ((Object)(object)currentCraftingStation != (Object)null && !currentCraftingStation.CheckUsable(localPlayer, false))) { return; } WornItems.Clear(); UsedStations.Clear(); ((Humanoid)localPlayer).GetInventory().GetWornItems(WornItems); int num = 0; ItemData val = null; foreach (ItemData wornItem in WornItems) { if (gui.CanRepair(wornItem)) { CraftingStation val2 = (CraftingStation)(((Object)(object)_lastNearbyStation != (Object)null) ? ((object)_lastNearbyStation) : ((object)currentCraftingStation)); ((Character)localPlayer).RaiseSkill((SkillType)107, 1f - wornItem.m_durability / wornItem.GetMaxDurability()); wornItem.m_durability = wornItem.GetMaxDurability(); if ((Object)(object)val2 != (Object)null && !UsedStations.Contains(val2)) { UsedStations.Add(val2); } Plugin.Debug("Repaired " + wornItem.m_shared.m_name + " at " + (((Object)(object)val2 != (Object)null) ? val2.m_name : "no station (no-cost mode)")); num++; val = wornItem; } } foreach (CraftingStation usedStation in UsedStations) { usedStation.m_repairItemDoneEffects.Create(((Component)usedStation).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); } UsedStations.Clear(); WornItems.Clear(); switch (num) { case 0: ((Character)localPlayer).Message((MessageType)2, "No more item to repair", 0, (Sprite)null, false); break; case 1: ((Character)localPlayer).Message((MessageType)2, Localization.instance.Localize("$msg_repaired", new string[1] { val.m_shared.m_name }), 0, (Sprite)null, false); break; default: ((Character)localPlayer).Message((MessageType)2, RepairRules.SeveralRepairedMessage(num), 0, (Sprite)null, false); break; } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }