using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GK2RustyToolDisposal.Configuration;
using GK2RustyToolDisposal.Helpers;
using HarmonyLib;
using LazyBearTechnology;
using Microsoft.CodeAnalysis;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("GK2RustyToolDisposal")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.1.0")]
[assembly: AssemblyInformationalVersion("0.2.1+d23762552e081496db0f717a75bb2a3b34529d41")]
[assembly: AssemblyProduct("GK2RustyToolDisposal")]
[assembly: AssemblyTitle("GK2RustyToolDisposal")]
[assembly: AssemblyVersion("0.2.1.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 GK2RustyToolDisposal
{
[BepInPlugin("de.w00dst0ckOo.gk2.rustytooldisposal", "Rusty Tool Disposal", "0.2.1")]
public sealed class Plugin : BaseUnityPlugin
{
public const string Guid = "de.w00dst0ckOo.gk2.rustytooldisposal";
public const string Name = "Rusty Tool Disposal";
public const string Version = "0.2.1";
private static readonly Dictionary<string, string> DebugStates = new Dictionary<string, string>(StringComparer.Ordinal);
private Harmony harmony;
internal static bool Ready { get; private set; }
internal static ManualLogSource Log { get; private set; }
internal static bool DebugEnabled
{
get
{
if (ModConfig.DebugLogging != null)
{
return ModConfig.DebugLogging.Value;
}
return false;
}
}
internal static void DebugState(string key, string message)
{
if (!DebugStates.TryGetValue(key, out var value) || !(value == message))
{
DebugStates[key] = message;
ManualLogSource log = Log;
if (log != null)
{
log.LogInfo((object)("[Debug] " + message));
}
}
}
private void Awake()
{
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
LegacyConfig legacyConfig = ConfigMigration.ReadLegacy(((BaseUnityPlugin)this).Config.ConfigFilePath);
ModConfig.Initialize(((BaseUnityPlugin)this).Config, legacyConfig);
if (DebugEnabled)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)"Debug logging enabled.");
}
if (legacyConfig.Found)
{
ConfigMigration.RemoveLegacySections(((BaseUnityPlugin)this).Config.ConfigFilePath);
}
try
{
harmony = new Harmony("de.w00dst0ckOo.gk2.rustytooldisposal");
harmony.PatchAll();
Ready = true;
((BaseUnityPlugin)this).Logger.LogInfo((object)"Rusty Tool Disposal 0.2.1 loaded.");
}
catch (Exception ex)
{
Ready = false;
Harmony obj = harmony;
if (obj != null)
{
obj.UnpatchSelf();
}
((BaseUnityPlugin)this).Logger.LogError((object)("Could not install discard patches; vanilla protection retained. " + ex));
}
}
private void OnDestroy()
{
Ready = false;
Harmony obj = harmony;
if (obj != null)
{
obj.UnpatchSelf();
}
DebugStates.Clear();
}
}
}
namespace GK2RustyToolDisposal.Patches
{
[HarmonyPatch]
internal static class DiscardEligibilityPatch
{
internal static IEnumerable<MethodBase> TargetMethods()
{
yield return AccessTools.Method(typeof(PlayerInventoryUIItemOpHandler), "OnPlayerInvItemPressed2", new Type[1] { typeof(UIItemCell) }, (Type[])null) ?? throw new MissingMethodException("OnPlayerInvItemPressed2");
yield return AccessTools.Method(typeof(PlayerInventoryUIItemOpHandler), "TryDestroyItem", new Type[1] { typeof(UIItemCell) }, (Type[])null) ?? throw new MissingMethodException("TryDestroyItem");
}
[HarmonyTranspiler]
internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
//IL_00d7: Expected O, but got Unknown
//IL_010e: Unknown result type (might be due to invalid IL or missing references)
//IL_0118: Expected O, but got Unknown
//IL_0120: Unknown result type (might be due to invalid IL or missing references)
//IL_012a: Expected O, but got Unknown
//IL_0132: Unknown result type (might be due to invalid IL or missing references)
//IL_013c: Expected O, but got Unknown
List<CodeInstruction> list = instructions.ToList();
MethodInfo getter = AccessTools.PropertyGetter(typeof(ItemDef), "CanNotBeDestroyed");
FieldInfo fieldInfo = AccessTools.Field(typeof(PlayerInventoryUIItemOpHandler), "playerData");
MethodInfo methodInfo = AccessTools.Method(typeof(RustyToolHelper), "CanNotBeDestroyed", (Type[])null, (Type[])null);
if (getter == null || fieldInfo == null || methodInfo == null || list.Count((CodeInstruction i) => CodeInstructionExtensions.Calls(i, getter)) != 1)
{
throw new InvalidOperationException("Expected exactly one native destroy check; refusing an unfamiliar method.");
}
List<CodeInstruction> list2 = new List<CodeInstruction>();
foreach (CodeInstruction item in list)
{
if (!CodeInstructionExtensions.Calls(item, getter))
{
list2.Add(item);
continue;
}
CodeInstruction val = new CodeInstruction(OpCodes.Ldarg_0, (object)null);
val.labels.AddRange(item.labels);
val.blocks.AddRange(item.blocks);
list2.Add(val);
list2.Add(new CodeInstruction(OpCodes.Ldfld, (object)fieldInfo));
list2.Add(new CodeInstruction(OpCodes.Ldarg_1, (object)null));
list2.Add(new CodeInstruction(OpCodes.Call, (object)methodInfo));
}
return list2;
}
}
}
namespace GK2RustyToolDisposal.Helpers
{
internal static class EquipmentSafetyHelper
{
private static bool Fail(Item item, string reason)
{
if (Plugin.DebugEnabled)
{
Plugin.DebugState("CanDispose:" + (((ObjectLinkedToDefinition<ItemDef>)(object)item)?.id ?? "null"), (((ObjectLinkedToDefinition<ItemDef>)(object)item)?.id ?? "null") + " BLOCKED: " + reason);
}
return false;
}
internal static bool SameInstance(Item left, Item right)
{
if (left == null || right == null)
{
return false;
}
if (left == right)
{
return true;
}
if (left.UniqueId != (SGuid)null && right.UniqueId != (SGuid)null && left.UniqueId.Guid != Guid.Empty)
{
return left.UniqueId.Guid == right.UniqueId.Guid;
}
return false;
}
internal static bool CanDispose(Item item, PlayerData player, PlayerController controller)
{
//IL_017a: Unknown result type (might be due to invalid IL or missing references)
//IL_0207: Unknown result type (might be due to invalid IL or missing references)
//IL_0212: Unknown result type (might be due to invalid IL or missing references)
//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
if (Plugin.DebugEnabled)
{
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogInfo((object)("[Debug] Checking disposal eligibility for " + (((ObjectLinkedToDefinition<ItemDef>)(object)item)?.id ?? "null")));
}
}
if (((item != null) ? item.UniqueId : null) == (SGuid)null || item.UniqueId.Guid == Guid.Empty)
{
return Fail(item, "item has no valid UniqueId");
}
object obj;
if (player == null)
{
obj = null;
}
else
{
Inventory inventory = player.Inventory;
obj = ((inventory != null) ? inventory.Data : null);
}
if (obj == null)
{
return Fail(item, "player inventory null");
}
Inventory toolBeltInventory = player.toolBeltInventory;
if (((toolBeltInventory != null) ? toolBeltInventory.Data : null) == null)
{
return Fail(item, "toolbelt inventory null");
}
Item val = default(Item);
if (!player.Inventory.Data.TryGetItemInInventoryByGUID(item.UniqueId.Id, ref val) || val != item)
{
return Fail(item, "item not owned by player inventory (UID/instance mismatch)");
}
Item val2 = default(Item);
if (player.toolBeltInventory.Data.TryGetItemInInventoryByGUID(item.UniqueId.Id, ref val2))
{
return Fail(item, "item is in toolbelt");
}
if (SameInstance(player.interactingItem, item))
{
return Fail(item, "item is interactingItem");
}
foreach (Item overheadItem in player.OverheadItems)
{
if (SameInstance(overheadItem, item))
{
return Fail(item, "item is overhead item");
}
}
Item itemByType = player.toolBeltInventory.GetItemByType(((ObjectLinkedToDefinition<ItemDef>)(object)item).Definition.type);
if (itemByType == null || itemByType.IsEmpty)
{
return Fail(item, "no toolbelt item of type " + ((object)Unsafe.As<ItemType, ItemType>(ref ((ObjectLinkedToDefinition<ItemDef>)(object)item).Definition.type)/*cast due to .constrained prefix*/).ToString());
}
if (SameInstance(itemByType, item))
{
return Fail(item, "toolbelt item is the same instance");
}
if (!RustyToolHelper.IsVerifiedUpgrade(((ObjectLinkedToDefinition<ItemDef>)(object)item).id, ((ObjectLinkedToDefinition<ItemDef>)(object)itemByType).id))
{
return Fail(item, "toolbelt item '" + ((ObjectLinkedToDefinition<ItemDef>)(object)itemByType).id + "' is not a verified upgrade of " + ((ObjectLinkedToDefinition<ItemDef>)(object)item).id);
}
if (((ObjectLinkedToDefinition<ItemDef>)(object)itemByType).Definition.type != ((ObjectLinkedToDefinition<ItemDef>)(object)item).Definition.type)
{
return Fail(item, "replacement type mismatch");
}
PlayerWorkComponent playerWorkComponent = controller.PlayerWorkComponent;
ToolComponent val3 = (((Object)(object)playerWorkComponent == (Object)null) ? null : playerWorkComponent.ToolComponent);
AttackComponent attackComponent = controller.AttackComponent;
FightingGameController instance = LazySingleton<FightingGameController>.Instance;
if (val3 == null)
{
return Fail(item, "ToolComponent null");
}
if ((Object)(object)attackComponent == (Object)null)
{
return Fail(item, "AttackComponent null");
}
if (!attackComponent.IsInitialized)
{
return Fail(item, "AttackComponent not initialized");
}
if ((Object)(object)instance == (Object)null)
{
return Fail(item, "FightingGameController null");
}
if (val3.IsActionActive)
{
return Fail(item, "tool action active");
}
if (val3.IsControlTakenByAnimation)
{
return Fail(item, "control taken by animation");
}
if (SameInstance(val3.ToolInUse, item))
{
return Fail(item, "item is ToolInUse");
}
if ((int)instance.CurrentFightState != 0)
{
return Fail(item, "fight state = " + ((object)instance.CurrentFightState/*cast due to .constrained prefix*/).ToString());
}
if ((Object)(object)attackComponent.weapon != (Object)null)
{
if (attackComponent.weapon.ItemDef == null)
{
return Fail(item, "weapon ItemDef null");
}
if (((BalanceBaseObject)attackComponent.weapon.ItemDef).id == ((ObjectLinkedToDefinition<ItemDef>)(object)item).id)
{
return Fail(item, "active weapon has same item id");
}
}
if (Plugin.DebugEnabled)
{
Plugin.DebugState("CanDispose:" + ((ObjectLinkedToDefinition<ItemDef>)(object)item).id, ((ObjectLinkedToDefinition<ItemDef>)(object)item).id + " ALLOWED");
}
return true;
}
}
internal static class RustyToolHelper
{
private static readonly HashSet<string> SupportedIds = new HashSet<string>(StringComparer.Ordinal) { "axe_0", "pickaxe_0", "hammer_0", "shovel_0", "sword_0", "armor_0", "armor_1" };
private static readonly string[] RequiredQuests = new string[5] { "1_intro_prison_find_pickaxe", "1_intro_prison_sarchphage_pickaxe_with", "2_intro_scout_axe", "3_intro_defence_no_armour", "4_intro_base_waking_up" };
private static bool loggedFailure;
private static bool Block(string id, string reason)
{
if (Plugin.DebugEnabled)
{
Plugin.DebugState(id, id + " BLOCKED: " + reason);
}
return true;
}
private static string JoinAny(IEnumerable values)
{
if (values == null)
{
return "null";
}
List<string> list = new List<string>();
foreach (object value in values)
{
list.Add((value == null) ? "null" : value.ToString());
}
return string.Join(",", list.ToArray());
}
internal static bool IsSupported(string id)
{
if (id != null)
{
return SupportedIds.Contains(id);
}
return false;
}
internal static bool IsVerifiedUpgrade(string rustyId, string upgradeId)
{
switch (rustyId)
{
case "armor_0":
if (!(upgradeId == "armor_1") && !(upgradeId == "armor_2"))
{
return upgradeId == "armor_3";
}
return true;
case "armor_1":
if (!(upgradeId == "armor_2"))
{
return upgradeId == "armor_3";
}
return true;
case "axe_0":
if (!(upgradeId == "axe_1") && !(upgradeId == "axe_2"))
{
return upgradeId == "axe_3";
}
return true;
case "pickaxe_0":
if (!(upgradeId == "pickaxe_1") && !(upgradeId == "pickaxe_2"))
{
return upgradeId == "pickaxe_3";
}
return true;
case "hammer_0":
if (!(upgradeId == "hammer_1") && !(upgradeId == "hammer_2"))
{
return upgradeId == "hammer_3";
}
return true;
case "shovel_0":
if (!(upgradeId == "shovel_1") && !(upgradeId == "shovel_2"))
{
return upgradeId == "shovel_3";
}
return true;
case "sword_0":
switch (upgradeId)
{
default:
return upgradeId == "sword_4";
case "sword_1":
case "sword_2":
case "sword_3":
return true;
}
default:
return false;
}
}
internal static bool HasCompletedRequiredQuests(QuestSystemData quests, bool tutorialActive)
{
if (tutorialActive || quests?.questCollection?.questsCache == null)
{
return false;
}
string[] requiredQuests = RequiredQuests;
foreach (string text in requiredQuests)
{
if (!quests.IsQuestInStatus(text, (QuestStatus)3))
{
return false;
}
}
return true;
}
internal static bool CanNotBeDestroyed(ItemDef definition, PlayerData player, UIItemCell cell)
{
if (definition == null)
{
return true;
}
if (!IsSupported(((BalanceBaseObject)definition).id))
{
return definition.CanNotBeDestroyed;
}
string id = ((BalanceBaseObject)definition).id;
try
{
Item val = (((Object)(object)cell == (Object)null) ? null : cell.DisplayingItem);
MainGame instance = MainGame.Instance;
GameSave val2 = (((Object)(object)instance == (Object)null) ? null : instance.GameSave);
PlayerController playerController = MainGame.PlayerController;
if (!Plugin.Ready)
{
return Block(id, "Plugin.Ready=false");
}
if (val == null || val.IsEmpty)
{
return Block(id, "item null/empty");
}
if (val.Count != 1)
{
return Block(id, "Count=" + val.Count);
}
if (((ObjectLinkedToDefinition<ItemDef>)(object)val).Definition != definition)
{
return Block(id, "Definition mismatch");
}
if (definition.isQuestItem)
{
return Block(id, "isQuestItem");
}
if (definition.isLinkedToWgo)
{
return Block(id, "isLinkedToWgo");
}
if (!definition.CanItemBeEquipped())
{
return Block(id, "CanItemBeEquipped=false");
}
if (player == null || val2 == null || (Object)(object)playerController == (Object)null)
{
return Block(id, "player/save/controller null");
}
if (player != val2.playerData || player != MainGame.PlayerData || player != playerController.PlayerData)
{
return Block(id, "PlayerData reference mismatch");
}
INetworkManager networkManager = LazyNetwork.NetworkManager;
if (Plugin.DebugEnabled)
{
Plugin.DebugState("net", "net: IsInitialized=" + LazyNetwork.IsInitialized + " manager=" + ((networkManager == null) ? "null" : "set") + " IsCoop=" + ((networkManager == null) ? "n/a" : networkManager.IsCoopGame.ToString()));
}
if (networkManager != null && networkManager.IsCoopGame)
{
return Block(id, "IsCoopGame");
}
if (!LazyNetwork.IsInitialized && networkManager != null)
{
return Block(id, "network manager exists but is not initialized");
}
if (player.isInTutorialMode)
{
return Block(id, "tutorial mode");
}
if (val2.questSystemData?.questCollection?.questsCache == null)
{
return Block(id, "questsCache null");
}
string[] requiredQuests = RequiredQuests;
foreach (string text in requiredQuests)
{
if (!val2.questSystemData.IsQuestInStatus(text, (QuestStatus)3))
{
return Block(id, "quest not Completed: " + text);
}
}
SurveyDef surveyDefForItemOrNull = GameBalance.GetSurveyDefForItemOrNull(id);
if (surveyDefForItemOrNull == null)
{
return Block(id, "no survey for item");
}
KnowledgeSystem knowledgeSystem = val2.knowledgeSystem;
if (Plugin.DebugEnabled)
{
Plugin.DebugState(id + ":survey", id + " survey: id=" + ((BalanceBaseObject)surveyDefForItemOrNull).id + " oneTime=" + surveyDefForItemOrNull.isOneTimeCraft + " sciFuel=" + surveyDefForItemOrNull.isScienceFuelCraft + " atStart=" + surveyDefForItemOrNull.surveyedAtStart + " craftsIn=" + JoinAny(((CraftDefBase)surveyDefForItemOrNull).craftsIn) + " surveyed=" + ((surveyDefForItemOrNull.SurveyedItem == null) ? "null" : surveyDefForItemOrNull.SurveyedItem.id) + " completedCrafts=" + ((knowledgeSystem?.oneTimeCompletedCrafts == null) ? "null" : knowledgeSystem.oneTimeCompletedCrafts.Contains(((BalanceBaseObject)surveyDefForItemOrNull).id).ToString()) + " IsSurveyCompleted=" + ((knowledgeSystem == null) ? "null" : knowledgeSystem.IsSurveyCompleted(surveyDefForItemOrNull).ToString()));
}
if (!StudyStateHelper.HasBeenStudied(id, surveyDefForItemOrNull, knowledgeSystem))
{
return Block(id, "not studied at the Study Table");
}
if (!EquipmentSafetyHelper.CanDispose(val, player, playerController))
{
return Block(id, "equipment safety check failed (see CanDispose)");
}
if (Plugin.DebugEnabled)
{
Plugin.DebugState(id, id + " ALLOWED");
}
return false;
}
catch (Exception ex)
{
if (!loggedFailure)
{
loggedFailure = true;
ManualLogSource log = Plugin.Log;
if (log != null)
{
log.LogWarning((object)("Could not prove discard safety; keeping the item protected. " + ex));
}
}
return true;
}
}
}
internal static class StudyStateHelper
{
internal static bool HasBeenStudied(string itemId, SurveyDef survey, KnowledgeSystem knowledge)
{
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
if (!RustyToolHelper.IsSupported(itemId) || survey == null || ((CraftDefBase)survey).needItems == null || knowledge?.oneTimeCompletedCrafts == null)
{
return false;
}
NeedItemData surveyedItem = survey.SurveyedItem;
if (((BalanceBaseObject)survey).id == "surv:" + itemId && survey.isOneTimeCraft && !survey.isScienceFuelCraft && !survey.surveyedAtStart && ((CraftDefBase)survey).craftsIn != null && ((CraftDefBase)survey).craftsIn.Contains("survey_wgo") && surveyedItem != null && (int)surveyedItem.groupType == 0 && string.Equals(surveyedItem.id, itemId, StringComparison.Ordinal) && knowledge.oneTimeCompletedCrafts.Contains(((BalanceBaseObject)survey).id))
{
return knowledge.IsSurveyCompleted(survey);
}
return false;
}
}
}
namespace GK2RustyToolDisposal.Configuration
{
internal sealed class LegacyConfig
{
internal bool Found;
internal bool DebugLogging;
}
internal static class ConfigMigration
{
private const string LegacyDebugSection = "Debug";
private const string LegacyDebugKey = "EnableDebugLogging";
internal static LegacyConfig ReadLegacy(string path)
{
LegacyConfig legacyConfig = new LegacyConfig();
if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
{
return legacyConfig;
}
try
{
string text = string.Empty;
string[] array = File.ReadAllLines(path);
for (int i = 0; i < array.Length; i++)
{
string text2 = array[i].Trim();
if (text2.Length == 0 || text2.StartsWith("#"))
{
continue;
}
if (text2.StartsWith("[") && text2.EndsWith("]"))
{
text = text2.Substring(1, text2.Length - 2);
continue;
}
int num = text2.IndexOf('=');
if (num >= 0)
{
string text3 = text2.Substring(0, num).Trim();
string value = text2.Substring(num + 1).Trim();
if (text == "Debug" && text3 == "EnableDebugLogging" && bool.TryParse(value, out var result))
{
legacyConfig.DebugLogging = result;
legacyConfig.Found = true;
}
}
}
return legacyConfig;
}
catch
{
return new LegacyConfig();
}
}
internal static void RemoveLegacySections(string path)
{
if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
{
return;
}
try
{
List<string> list = new List<string>();
string text = string.Empty;
string[] array = File.ReadAllLines(path);
foreach (string text2 in array)
{
string text3 = text2.Trim();
if (text3.StartsWith("[") && text3.EndsWith("]"))
{
text = text3.Substring(1, text3.Length - 2);
}
if (!(text == "Debug"))
{
list.Add(text2);
}
}
File.WriteAllLines(path, list);
}
catch
{
}
}
}
internal static class ModConfig
{
private const string SectionDebug = "90 - Debug";
private static ConfigFile configFile;
internal static ConfigEntry<bool> DebugLogging { get; private set; }
internal static void Initialize(ConfigFile config, LegacyConfig legacy)
{
configFile = config;
DebugLogging = config.Bind<bool>("90 - Debug", "EnableDebugLogging", false, "Enables detailed diagnostic logging explaining why rusty equipment is protected or allowed.");
if (legacy != null && legacy.Found)
{
DebugLogging.Value = legacy.DebugLogging;
SaveIfAutoSaveDisabled();
}
}
internal static void SaveIfAutoSaveDisabled()
{
if (configFile != null && !configFile.SaveOnConfigSet)
{
configFile.Save();
}
}
}
}