Decompiled source of Obliterator Tweaks v1.0.0

BepInEx/plugins/ObliteratorTweaks/ObliteratorTweaks.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ObliteratorTweaks")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ObliteratorTweaks")]
[assembly: AssemblyTitle("ObliteratorTweaks")]
[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.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 ObliteratorTweaks
{
	[BepInPlugin("com.kingangel0475.obliteratortweaks", "Obliterator Tweaks", "1.0.0")]
	public sealed class ObliteratorTweaksPlugin : BaseUnityPlugin
	{
		private sealed class TradeDefinition
		{
			public string Id { get; }

			public string Section { get; }

			public string?[] Inputs { get; }

			public ConfigEntry<int>?[] InputAmounts { get; }

			public string OutputPrefab { get; }

			public ConfigEntry<int> OutputAmount { get; }

			public KnowledgeKind? Knowledge { get; }

			public string Label { get; }

			public string? KnowledgePrefab { get; set; }

			public string? PunishmentPrefab { get; set; }

			public ConfigEntry<int>? PunishmentAmount { get; set; }

			public TradeDefinition(string id, string section, string?[] inputs, ConfigEntry<int>?[] inputAmounts, string outputPrefab, ConfigEntry<int> outputAmount, KnowledgeKind? knowledge, string label)
			{
				Id = id;
				Section = section;
				Inputs = inputs;
				InputAmounts = inputAmounts;
				OutputPrefab = outputPrefab;
				OutputAmount = outputAmount;
				Knowledge = knowledge;
				Label = label;
			}
		}

		private sealed class IdolSacrificeDefinition
		{
			public string Id { get; }

			public string MaterialPrefab { get; }

			public ConfigEntry<int> MaterialAmount { get; }

			public string WoodPrefab { get; }

			public ConfigEntry<int> WoodAmount { get; }

			public ConfigEntry<int> SurtlingCoreAmount { get; }

			public ConfigEntry<int> EquipmentAmount { get; }

			public string BattleIdolPrefab { get; }

			public ConfigEntry<int> BattleIdolAmount { get; }

			public string ProtectionIdolPrefab { get; }

			public ConfigEntry<int> ProtectionIdolAmount { get; }

			public IdolSacrificeDefinition(string id, string materialPrefab, ConfigEntry<int> materialAmount, string woodPrefab, ConfigEntry<int> woodAmount, ConfigEntry<int> surtlingCoreAmount, ConfigEntry<int> equipmentAmount, string battleIdolPrefab, ConfigEntry<int> battleIdolAmount, string protectionIdolPrefab, ConfigEntry<int> protectionIdolAmount)
			{
				Id = id;
				MaterialPrefab = materialPrefab;
				MaterialAmount = materialAmount;
				WoodPrefab = woodPrefab;
				WoodAmount = woodAmount;
				SurtlingCoreAmount = surtlingCoreAmount;
				EquipmentAmount = equipmentAmount;
				BattleIdolPrefab = battleIdolPrefab;
				BattleIdolAmount = battleIdolAmount;
				ProtectionIdolPrefab = protectionIdolPrefab;
				ProtectionIdolAmount = protectionIdolAmount;
			}
		}

		private sealed class IdolConversionBinding
		{
			public string ConversionId { get; }

			public ConversionState Conversion { get; }

			public IdolSacrificeDefinition Sacrifice { get; }

			public string GearPrefab { get; }

			public string IdolPrefab { get; }

			public string CounterId { get; }

			public IdolConversionBinding(string id, ConversionState conversion, IdolSacrificeDefinition sacrifice, string gearPrefab, string idolPrefab, bool battle)
			{
				ConversionId = id;
				Conversion = conversion;
				Sacrifice = sacrifice;
				GearPrefab = gearPrefab;
				IdolPrefab = idolPrefab;
				CounterId = sacrifice.Id + (battle ? "Battle" : "Protection");
			}
		}

		private sealed class ConversionState
		{
			public string Id { get; }

			public object Conversion { get; }

			public ItemDrop Output { get; set; }

			public ConfigEntry<int> OutputAmount { get; set; }

			public List<int> InputAmounts { get; }

			public List<ConfigEntry<int>?> InputSettings { get; }

			public List<object> RequirementObjects { get; }

			public ConversionState(string id, object conversion, ItemDrop output, ConfigEntry<int> outputAmount, int inputs)
			{
				Id = id;
				Conversion = conversion;
				Output = output;
				OutputAmount = outputAmount;
				InputAmounts = new List<int>(inputs);
				InputSettings = new List<ConfigEntry<int>>(inputs);
				RequirementObjects = new List<object>(inputs);
			}
		}

		private sealed class PendingPunishment
		{
			public ConversionState Conversion { get; }

			public ItemDrop OriginalOutput { get; }

			public ConfigEntry<int> OriginalOutputAmount { get; }

			public PendingPunishment(ConversionState conversion, ItemDrop originalOutput, ConfigEntry<int> originalOutputAmount)
			{
				Conversion = conversion;
				OriginalOutput = originalOutput;
				OriginalOutputAmount = originalOutputAmount;
			}
		}

		private enum KnowledgeKind
		{
			Material,
			Recipe
		}

		private enum TrophyRule
		{
			Boss,
			LargeCreature,
			OtherCreature
		}

		private enum IdolSacrificeKind
		{
			Battle,
			Protection
		}

		private sealed class ConfigurationManagerAttributes
		{
			public string? DispName;

			public int? Order;
		}

		[HarmonyPatch(typeof(Incinerator), "RPC_RequestIncinerate")]
		private static class IncinerateRequestPatch
		{
			private static bool Prefix(Incinerator __instance, long uid)
			{
				return PatchRequest(__instance, uid);
			}
		}

		[HarmonyPatch(typeof(Incinerator), "Incinerate")]
		private static class IncinerateRoutinePatch
		{
			private static void Postfix(Incinerator __instance, ref IEnumerator __result)
			{
				PatchIncinerate(__instance, ref __result);
			}
		}

		[HarmonyPatch(typeof(Incinerator), "RPC_IncinerateRespons")]
		private static class IncinerateResponsePatch
		{
			private static bool Prefix(int response)
			{
				return PatchResponse(response);
			}
		}

		public const string PluginGuid = "com.kingangel0475.obliteratortweaks";

		public const string PluginName = "Obliterator Tweaks";

		public const string PluginVersion = "1.0.0";

		private const int ResponseKnowledgeMissing = 40;

		private const int ResponseThorPunishmentPending = 41;

		private const int AttemptsBeforePunishment = 5;

		private const string ZdoAttemptPrefix = "ObliteratorTweaks.Attempts.";

		private static readonly FieldInfo ConversionListField = AccessTools.Field(typeof(Incinerator), "m_conversions");

		private static readonly FieldInfo ResultField = AccessTools.Field(typeof(IncineratorConversion), "m_result");

		private static readonly FieldInfo ResultAmountField = AccessTools.Field(typeof(IncineratorConversion), "m_resultAmount");

		private static readonly FieldInfo PriorityField = AccessTools.Field(typeof(IncineratorConversion), "m_priority");

		private static readonly FieldInfo OnlyOneField = AccessTools.Field(typeof(IncineratorConversion), "m_requireOnlyOneIngredient");

		private static readonly FieldInfo RequirementsField = AccessTools.Field(typeof(IncineratorConversion), "m_requirements");

		private static readonly FieldInfo RequirementItemField = AccessTools.Field(typeof(Requirement), "m_resItem");

		private static readonly FieldInfo RequirementAmountField = AccessTools.Field(typeof(Requirement), "m_amount");

		private static readonly FieldInfo ContainerField = AccessTools.Field(typeof(Incinerator), "m_container");

		private static readonly FieldInfo NetworkViewField = AccessTools.Field(typeof(Incinerator), "m_nview");

		private static readonly FieldInfo ObjectDbItemsField = AccessTools.Field(typeof(ObjectDB), "m_items");

		private static ObliteratorTweaksPlugin? _instance;

		private static bool _clientThorMessagePending;

		private readonly Dictionary<string, TradeDefinition> _trades = new Dictionary<string, TradeDefinition>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<Incinerator, Dictionary<string, ConversionState>> _conversions = new Dictionary<Incinerator, Dictionary<string, ConversionState>>();

		private readonly Dictionary<Incinerator, Dictionary<string, IdolConversionBinding>> _idolConversions = new Dictionary<Incinerator, Dictionary<string, IdolConversionBinding>>();

		private readonly Dictionary<Incinerator, PendingPunishment> _pendingPunishments = new Dictionary<Incinerator, PendingPunishment>();

		private readonly List<IdolSacrificeDefinition> _idolSacrifices = new List<IdolSacrificeDefinition>();

		private readonly HashSet<string> _loggedMissingItems = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private ConfigEntry<int>? _woodCoalAmount;

		private ConfigEntry<int>? _bossCoalAmount;

		private ConfigEntry<int>? _largeCreatureCoalAmount;

		private ConfigEntry<int>? _otherTrophyCoalAmount;

		private ConfigEntry<int>? _idolThorPunishmentAmount;

		private bool _isSpanish;

		private int _displayOrder;

		private static readonly HashSet<string> BossTrophies = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "TrophyEikthyr", "TrophyTheElder", "TrophyBonemass", "TrophyDragonQueen", "TrophyGoblinKing", "TrophySeekerQueen", "TrophyFader" };

		private static readonly HashSet<string> LargeTrophies = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
		{
			"TrophyForestTroll", "TrophyFrostTroll", "TrophyBjorn", "TrophyBjornUndead", "TrophySerpent", "TrophyAbomination", "TrophySGolem", "TrophyLox", "TrophyGoblinBrute", "TrophyGjall",
			"TrophySeekerBrute", "TrophyBonemawSerpent", "TrophyMorgen", "TrophyBarka", "TrophyGammeltroll"
		};

		private void Awake()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			_instance = this;
			DetectLanguage();
			BindSettings();
			new Harmony("com.kingangel0475.obliteratortweaks").PatchAll(typeof(ObliteratorTweaksPlugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)Text("Obliterator Tweaks loaded. Vanilla conversions are retained, with configurable creature, resource and Idol offerings.", "Obliterator Tweaks está cargado. Conserva los intercambios vanilla y añade ofrendas configurables de criaturas, recursos e ídolos."));
		}

		private void DetectLanguage()
		{
			string text = null;
			try
			{
				Localization instance = Localization.instance;
				text = ((instance != null) ? instance.GetSelectedLanguage() : null);
			}
			catch
			{
			}
			if (!string.IsNullOrWhiteSpace(text))
			{
				_isSpanish = text.StartsWith("Spanish", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Español", StringComparison.OrdinalIgnoreCase) || text.StartsWith("es", StringComparison.OrdinalIgnoreCase);
			}
			else
			{
				_isSpanish = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName.Equals("es", StringComparison.OrdinalIgnoreCase) || CultureInfo.CurrentCulture.TwoLetterISOLanguageName.Equals("es", StringComparison.OrdinalIgnoreCase);
			}
		}

		private string Text(string english, string spanish)
		{
			return _isSpanish ? spanish : english;
		}

		private void BindSettings()
		{
			string section = Text("WOOD", "MADERA");
			string section2 = Text("TROPHIES/BOSSES", "TROFEOS/JEFES");
			string section3 = Text("TROPHIES/LARGE CREATURES", "TROFEOS/CRIATURAS GRANDES");
			string section4 = Text("TROPHIES/OTHER CREATURES", "TROFEOS/OTRAS CRIATURAS");
			_woodCoalAmount = BindAmount(section, "Coal per Wood offering", 1, "Coal returned for each conversion batch of wood.", "Carbón que devuelve cada lote de madera.", Text("Coal returned", "Carbón devuelto"));
			ConfigEntry<int> val = BindAmount(section, "Wood required", 2, "Wood required to receive the coal amount above. Vanilla default is 5.", "Madera necesaria para recibir el carbón anterior. El valor vanilla es 5.", Text("Wood required", "Madera necesaria"));
			BindTrade("WoodToCoal", section, new string[1] { "Wood" }, new ConfigEntry<int>[1] { val }, "Coal", _woodCoalAmount, null, "2 Wood → 1 Coal", "2 maderas → 1 carbón");
			_bossCoalAmount = BindAmount(section2, "Coal per boss trophy", 25, "Coal returned for each boss trophy. Set to 0 to discard the trophy without a reward.", "Carbón por cada trofeo de jefe. Pon 0 para sacrificarlo sin recompensa.", Text("Coal per boss trophy", "Carbón por trofeo de jefe"));
			_largeCreatureCoalAmount = BindAmount(section3, "Coal per large creature trophy", 15, "Coal returned for each large-creature trophy.", "Carbón que devuelve cada trofeo de criatura grande.", Text("Coal per large creature trophy", "Carbón por trofeo de criatura grande"));
			_otherTrophyCoalAmount = BindAmount(section4, "Coal per other creature trophy", 5, "Coal returned for each other creature trophy.", "Carbón que devuelve cada trofeo de las demás criaturas.", Text("Coal per other creature trophy", "Carbón por trofeo de otras criaturas"));
			BindOffer("SurtlingTrophy", Text("ANIMALS & CREATURES", "ANIMALES Y CRIATURAS"), "Surtling trophy", "TrophySurtling", 1, "Surtling Core", "SurtlingCore", 2);
			BindOffer("BoarTrophy", Text("ANIMALS & CREATURES", "ANIMALES Y CRIATURAS"), "Boar trophy", "TrophyBoar", 1, "Leather Scraps", "LeatherScraps", 5);
			BindOffer("DeerTrophy", Text("ANIMALS & CREATURES", "ANIMALES Y CRIATURAS"), "Deer trophy", "TrophyDeer", 1, "Deer Hide", "DeerHide", 4);
			BindOffer("HareTrophy", Text("ANIMALS & CREATURES", "ANIMALES Y CRIATURAS"), "Hare trophy", "TrophyHare", 1, "Hare Meat", "HareMeat", 4);
			BindOffer("BoarMeatToTrophy", Text("ANIMALS & CREATURES", "ANIMALES Y CRIATURAS"), "Boar Meat", "RawMeat", 5, "Boar trophy", "TrophyBoar", 1);
			BindOffer("DeerMeatToTrophy", Text("ANIMALS & CREATURES", "ANIMALES Y CRIATURAS"), "Deer Meat", "DeerMeat", 5, "Deer trophy", "TrophyDeer", 1);
			BindOffer("HareMeatToTrophy", Text("ANIMALS & CREATURES", "ANIMALES Y CRIATURAS"), "Hare Meat", "HareMeat", 5, "Hare trophy", "TrophyHare", 1);
			BindGatedOffer("StoneToBlackMarble", Text("KNOWLEDGE SACRIFICES/RESOURCES", "SACRIFICIOS CON CONOCIMIENTO/RECURSOS"), "Stone", "Stone", 150, "Black Marble", "BlackMarble", 50, KnowledgeKind.Material, "BlackMarble", "Stone", 3);
			BindGatedOffer("CrystalToFireWine", Text("KNOWLEDGE SACRIFICES/RESOURCES", "SACRIFICIOS CON CONOCIMIENTO/RECURSOS"), "Crystal", "Crystal", 150, "Fire Resistance Barley Wine", "BarleyWine", 10, KnowledgeKind.Recipe, "BarleyWineBase", "Coal", 3);
			BindGatedOffer("RaspberryToMinorHealing", Text("KNOWLEDGE SACRIFICES/MEADS", "SACRIFICIOS CON CONOCIMIENTO/MEADS"), "Raspberry", "Raspberry", 50, "Minor Healing Mead", "MeadHealthMinor", 5, KnowledgeKind.Recipe, "MeadBaseHealthMinor", "Coal", 3);
			BindGatedOffer("YellowMushroomToMinorStamina", Text("KNOWLEDGE SACRIFICES/MEADS", "SACRIFICIOS CON CONOCIMIENTO/MEADS"), "Yellow Mushroom", "MushroomYellow", 50, "Minor Stamina Mead", "MeadStaminaMinor", 5, KnowledgeKind.Recipe, "MeadBaseStaminaMinor", "Coal", 3);
			BindGatedOffer("HoneyToMediumStamina", Text("KNOWLEDGE SACRIFICES/MEADS", "SACRIFICIOS CON CONOCIMIENTO/MEADS"), "Honey", "Honey", 50, "Medium Stamina Mead", "MeadStaminaMedium", 5, KnowledgeKind.Recipe, "MeadBaseStaminaMedium", "Coal", 3);
			BindGatedOffer("MajorToMediumHealing", Text("KNOWLEDGE SACRIFICES/MEADS", "SACRIFICIOS CON CONOCIMIENTO/MEADS"), "Major Healing Mead", "MeadHealthMajor", 50, "Medium Healing Mead", "MeadHealthMedium", 5, KnowledgeKind.Recipe, "MeadBaseHealthMedium", "Coal", 3);
			BindGatedOffer("BloodClotToMajorHealing", Text("KNOWLEDGE SACRIFICES/MEADS", "SACRIFICIOS CON CONOCIMIENTO/MEADS"), "Blood Clot", "Bloodclot", 50, "Major Healing Mead", "MeadHealthMajor", 5, KnowledgeKind.Recipe, "MeadBaseHealthMajor", "Coal", 3);
			BindGatedOffer("GreydwarfEyeToFrostMead", Text("KNOWLEDGE SACRIFICES/MEADS", "SACRIFICIOS CON CONOCIMIENTO/MEADS"), "Greydwarf Eye", "GreydwarfEye", 150, "Frost Resistance Mead", "MeadFrostResist", 10, KnowledgeKind.Recipe, "MeadBaseFrostResist", "Coal", 3);
			BindOffer("ResinBirchToAcorns", Text("SPECIAL OFFERINGS/NATURE", "OFRENDAS ESPECIALES/NATURALEZA"), "Resin", "Resin", 50, "Acorns", "Acorn", 5, "Fine Wood", "FineWood", 50);
			BindIdolSacrifices();
		}

		private void BindIdolSacrifices()
		{
			BindIdolSacrifice("Meadows", "Meadows", "Praderas", "Flint", "Flint", 2, "Wood", "Wood", 5, "Upgrader0Weapon", "Upgrader0Armor");
			BindIdolSacrifice("BlackForest", "Black Forest", "Bosque Negro", "Bronze", "Bronze", 2, "Core Wood", "RoundLog", 5, "Upgrader1Weapon", "Upgrader1Armor");
			BindIdolSacrifice("Swamp", "Swamp", "Pantano", "Iron", "Iron", 2, "Ancient Bark", "ElderBark", 5, "Upgrader2Weapon", "Upgrader2Armor");
			BindIdolSacrifice("Mountains", "Mountains", "Montañas", "Silver", "Silver", 2, "Wood", "Wood", 5, "Upgrader3Weapon", "Upgrader3Armor");
			BindIdolSacrifice("Plains", "Plains", "Llanuras", "Black Metal", "BlackMetal", 2, "Fine Wood", "FineWood", 5, "Upgrader4Weapon", "Upgrader4Armor");
			BindIdolSacrifice("Mistlands", "Mistlands", "Tierras Brumosas", "Iron", "Iron", 4, "Yggdrasil Wood", "YggdrasilWood", 5, "Upgrader5Weapon", "Upgrader5Armor");
			BindIdolSacrifice("Ashlands", "Ashlands", "Tierras de Ceniza", "Flametal", "Flametal", 2, "Ashwood", "Ashwood", 5, "Upgrader6Weapon", "Upgrader6Armor");
			BindIdolSacrifice("DeepNorth", "Deep North", "Norte Profundo", "Bloodgold", "Gold", 2, "Timberwood", "Frostwood", 5, "Upgrader7Weapon", "Upgrader7Armor");
			_idolThorPunishmentAmount = BindAmount(Text("IDOL SACRIFICES/THOR'S WRATH", "SACRIFICIOS DE ÍDOLOS/CASTIGO DE THOR"), "Coal returned when an idol is still unknown", 3, "Coal returned by Thor's punishment after five attempts to sacrifice an idol you have not discovered.", "Carbón que devuelve el castigo de Thor tras cinco intentos de sacrificar un ídolo aún no descubierto.", Text("Coal returned by Thor", "Carbón devuelto por Thor"));
		}

		private void BindIdolSacrifice(string id, string englishBiome, string spanishBiome, string materialName, string materialPrefab, int materialAmount, string woodName, string woodPrefab, int woodAmount, string battleIdolPrefab, string protectionIdolPrefab)
		{
			string section = Text("IDOL SACRIFICES/" + englishBiome.ToUpperInvariant(), "SACRIFICIOS DE ÍDOLOS/" + spanishBiome.ToUpperInvariant());
			ConfigEntry<int> materialAmount2 = BindAmount(section, id + ".MaterialAmount", materialAmount, "Required amount of " + materialName + ". Set to 0 to disable these idol sacrifices.", "Cantidad necesaria de " + TranslateItem(materialName) + ". Pon 0 para desactivar estos sacrificios de ídolos.", (_isSpanish ? TranslateItem(materialName) : materialName) + Text(" — required", " — cantidad necesaria"));
			ConfigEntry<int> woodAmount2 = BindAmount(section, id + ".WoodAmount", woodAmount, "Required amount of " + woodName + ". Set to 0 to disable these idol sacrifices.", "Cantidad necesaria de " + TranslateItem(woodName) + ". Pon 0 para desactivar estos sacrificios de ídolos.", (_isSpanish ? TranslateItem(woodName) : woodName) + Text(" — required", " — cantidad necesaria"));
			ConfigEntry<int> surtlingCoreAmount = BindAmount(section, id + ".SurtlingCoreAmount", 1, "Surtling Cores required for each idol sacrifice.", "Núcleos de Surtling necesarios por sacrificio de ídolo.", Text("Surtling Cores — required", "Núcleos de Surtling — cantidad necesaria"));
			ConfigEntry<int> equipmentAmount = BindAmount(section, id + ".EquipmentAmount", 1, "Weapons are consumed for Battle Idols; armor and shields are consumed for Protection Idols.", "Se consumen armas para los ídolos de batalla; armaduras y escudos para los ídolos de protección.", Text("Weapon / armor / shield — sacrificed", "Arma / armadura / escudo — sacrificado"));
			ConfigEntry<int> battleIdolAmount = BindAmount(section, id + ".BattleIdolOutput", 1, "Battle Idols returned per offering. Requires having discovered this Idol in normal loot.", "Ídolos de batalla devueltos por ofrenda. Requiere haber descubierto este ídolo en el botín normal.", IdolDisplayName(battleIdolPrefab) + Text(" — returned", " — cantidad devuelta"));
			ConfigEntry<int> protectionIdolAmount = BindAmount(section, id + ".ProtectionIdolOutput", 1, "Protection Idols returned per offering. Requires having discovered this Idol in normal loot.", "Ídolos de protección devueltos por ofrenda. Requiere haber descubierto este ídolo en el botín normal.", IdolDisplayName(protectionIdolPrefab) + Text(" — returned", " — cantidad devuelta"));
			_idolSacrifices.Add(new IdolSacrificeDefinition(id, materialPrefab, materialAmount2, woodPrefab, woodAmount2, surtlingCoreAmount, equipmentAmount, battleIdolPrefab, battleIdolAmount, protectionIdolPrefab, protectionIdolAmount));
		}

		private ConfigEntry<int> BindAmount(string section, string key, int defaultValue, string englishDescription, string spanishDescription, string displayName)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			return ((BaseUnityPlugin)this).Config.Bind<int>(section, key, defaultValue, new ConfigDescription(Text(englishDescription, spanishDescription), (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					DispName = displayName,
					Order = _displayOrder++
				}
			}));
		}

		private void BindOffer(string id, string section, string inputName, string inputPrefab, int inputAmount, string outputName, string outputPrefab, int outputAmount, string? secondInputName = null, string? secondInputPrefab = null, int secondInputAmount = 0)
		{
			string englishLabel = ((secondInputName == null) ? (inputAmount + " " + inputName + " → " + outputAmount + " " + outputName) : (inputAmount + " " + inputName + " + " + secondInputAmount + " " + secondInputName + " → " + outputAmount + " " + outputName));
			string spanishLabel = ((secondInputName == null) ? (inputAmount + " " + TranslateItem(inputName) + " → " + outputAmount + " " + TranslateItem(outputName)) : (inputAmount + " " + TranslateItem(inputName) + " + " + secondInputAmount + " " + TranslateItem(secondInputName) + " → " + outputAmount + " " + TranslateItem(outputName)));
			string text = (_isSpanish ? TranslateItem(inputName) : inputName);
			string text2 = (_isSpanish ? TranslateItem(outputName) : outputName);
			ConfigEntry<int> val = BindAmount(section, id + ".InputAmount", inputAmount, "Items required per exchange. Set to 0 to disable this offering.", "Objetos necesarios por intercambio. Pon 0 para desactivar esta ofrenda.", ((secondInputName == null) ? text : (text + " + " + (_isSpanish ? TranslateItem(secondInputName) : secondInputName))) + Text(" — required", " — cantidad necesaria"));
			ConfigEntry<int> val2 = null;
			if (secondInputPrefab != null)
			{
				val2 = BindAmount(section, id + ".SecondInputAmount", secondInputAmount, "Second ingredient required per exchange.", "Segundo ingrediente necesario por intercambio.", (_isSpanish ? TranslateItem(secondInputName) : secondInputName) + Text(" — required", " — cantidad necesaria"));
			}
			ConfigEntry<int> outputAmount2 = BindAmount(section, id + ".OutputAmount", outputAmount, "Items returned per exchange. Set to 0 to consume the offering without a reward.", "Objetos que devuelve cada intercambio. Pon 0 para consumir la ofrenda sin recompensa.", text2 + Text(" — returned", " — cantidad devuelta"));
			BindTrade(id, section, new string[2] { inputPrefab, secondInputPrefab }, new ConfigEntry<int>[2] { val, val2 }, outputPrefab, outputAmount2, null, englishLabel, spanishLabel);
		}

		private void BindGatedOffer(string id, string section, string inputName, string inputPrefab, int inputAmount, string outputName, string outputPrefab, int outputAmount, KnowledgeKind knowledgeKind, string knowledgePrefab, string punishmentPrefab, int punishmentAmount)
		{
			string englishLabel = inputAmount + " " + inputName + " → " + outputAmount + " " + outputName;
			string spanishLabel = inputAmount + " " + TranslateItem(inputName) + " → " + outputAmount + " " + TranslateItem(outputName);
			string text = (_isSpanish ? TranslateItem(inputName) : inputName);
			string text2 = (_isSpanish ? TranslateItem(outputName) : outputName);
			ConfigEntry<int> val = BindAmount(section, id + ".InputAmount", inputAmount, "Items required per exchange. Set to 0 to disable this offering.", "Objetos necesarios por intercambio. Pon 0 para desactivar esta ofrenda.", text + Text(" — required", " — cantidad necesaria"));
			ConfigEntry<int> outputAmount2 = BindAmount(section, id + ".OutputAmount", outputAmount, "Items returned per exchange. Set to 0 to consume the offering without a reward.", "Objetos que devuelve cada intercambio. Pon 0 para consumir la ofrenda sin recompensa.", text2 + Text(" — returned", " — cantidad devuelta"));
			TradeDefinition tradeDefinition = BindTrade(id, section, new string[1] { inputPrefab }, new ConfigEntry<int>[1] { val }, outputPrefab, outputAmount2, knowledgeKind, englishLabel, spanishLabel);
			tradeDefinition.KnowledgePrefab = knowledgePrefab;
			tradeDefinition.PunishmentPrefab = punishmentPrefab;
			tradeDefinition.PunishmentAmount = BindAmount(section, id + ".ThorPunishmentAmount", punishmentAmount, "Items returned on the fifth activation if the required knowledge is still missing.", "Objetos que devuelve en la quinta activación si aún falta el conocimiento.", punishmentPrefab + Text(" — Thor's punishment", " — castigo de Thor"));
		}

		private TradeDefinition BindTrade(string id, string section, string?[] inputPrefabs, ConfigEntry<int>?[] inputAmounts, string outputPrefab, ConfigEntry<int> outputAmount, KnowledgeKind? knowledgeKind, string englishLabel, string spanishLabel)
		{
			TradeDefinition tradeDefinition = new TradeDefinition(id, section, inputPrefabs, inputAmounts, outputPrefab, outputAmount, knowledgeKind, Text(englishLabel, spanishLabel));
			_trades.Add(id, tradeDefinition);
			return tradeDefinition;
		}

		private string TranslateItem(string item)
		{
			return item switch
			{
				"Surtling trophy" => "trofeo de Surtling", 
				"Surtling Core" => "núcleo de Surtling", 
				"Boar trophy" => "trofeo de jabalí", 
				"Leather Scraps" => "restos de cuero", 
				"Deer trophy" => "trofeo de ciervo", 
				"Deer Hide" => "piel de ciervo", 
				"Hare trophy" => "trofeo de liebre", 
				"Hare Meat" => "carne de liebre", 
				"Boar Meat" => "carne de jabalí", 
				"Deer Meat" => "carne de ciervo", 
				"Stone" => "piedra", 
				"Black Marble" => "mármol negro", 
				"Crystal" => "cristal", 
				"Fire Resistance Barley Wine" => "vino de cebada resistente al fuego", 
				"Raspberry" => "frambuesa", 
				"Minor Healing Mead" => "hidromiel de curación menor", 
				"Yellow Mushroom" => "seta amarilla", 
				"Minor Stamina Mead" => "hidromiel de resistencia menor", 
				"Honey" => "miel", 
				"Medium Stamina Mead" => "hidromiel de resistencia media", 
				"Major Healing Mead" => "hidromiel de curación mayor", 
				"Medium Healing Mead" => "hidromiel de curación media", 
				"Blood Clot" => "coágulo de sangre", 
				"Greydwarf Eye" => "ojo de enano gris", 
				"Frost Resistance Mead" => "hidromiel de resistencia a las heladas", 
				"Resin" => "resina", 
				"Fine Wood" => "madera noble", 
				"Acorns" => "bellotas", 
				"Wood" => "madera", 
				"Flint" => "pedernal", 
				"Core Wood" => "madera de núcleo", 
				"Ancient Bark" => "madera ancestral", 
				"Silver" => "plata", 
				"Black Metal" => "metal negro", 
				"Yggdrasil Wood" => "madera de Yggdrasil", 
				"Flametal" => "flametal", 
				"Ashwood" => "madera de ceniza", 
				"Bloodgold" => "oro de sangre", 
				"Timberwood" => "madera de construcción", 
				"Surtling Cores" => "núcleos de Surtling", 
				"Coal" => "carbón", 
				"BlackMarble" => "mármol negro", 
				"BarleyWine" => "vino de cebada resistente al fuego", 
				"MeadHealthMinor" => "hidromiel de curación menor", 
				"MeadStaminaMinor" => "hidromiel de resistencia menor", 
				"MeadStaminaMedium" => "hidromiel de resistencia media", 
				"MeadHealthMajor" => "hidromiel de curación mayor", 
				"MeadHealthMedium" => "hidromiel de curación media", 
				"MeadFrostResist" => "hidromiel de resistencia a las heladas", 
				_ => item, 
			};
		}

		private string IdolDisplayName(string prefab)
		{
			if (1 == 0)
			{
			}
			string text;
			switch (prefab)
			{
			case "Upgrader0Weapon":
			case "Upgrader0Armor":
				text = Text("Wooden", "de Madera");
				break;
			case "Upgrader1Weapon":
			case "Upgrader1Armor":
				text = Text("Bronze", "de Bronce");
				break;
			case "Upgrader2Weapon":
			case "Upgrader2Armor":
				text = Text("Iron", "de Hierro");
				break;
			case "Upgrader3Weapon":
			case "Upgrader3Armor":
				text = Text("Silver", "de Plata");
				break;
			case "Upgrader4Weapon":
			case "Upgrader4Armor":
				text = Text("Black Metal", "de Metal Negro");
				break;
			case "Upgrader5Weapon":
			case "Upgrader5Armor":
				text = Text("Black Marble", "de Mármol Negro");
				break;
			case "Upgrader6Weapon":
			case "Upgrader6Armor":
				text = Text("Flametal", "de Flametal");
				break;
			case "Upgrader7Weapon":
			case "Upgrader7Armor":
				text = Text("Bloodgold", "de Oro de Sangre");
				break;
			default:
				text = prefab;
				break;
			}
			if (1 == 0)
			{
			}
			string text2 = text;
			string text3 = (prefab.EndsWith("Weapon", StringComparison.OrdinalIgnoreCase) ? Text("Battle Idol", "Ídolo de Batalla") : Text("Protection Idol", "Ídolo de Protección"));
			return _isSpanish ? (text3 + " " + text2) : (text2 + " " + text3);
		}

		private void EnsureConversions(Incinerator incinerator)
		{
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			if (!_conversions.TryGetValue(incinerator, out Dictionary<string, ConversionState> value))
			{
				value = new Dictionary<string, ConversionState>(StringComparer.OrdinalIgnoreCase);
				_conversions.Add(incinerator, value);
			}
			if (!(ConversionListField.GetValue(incinerator) is IList conversions))
			{
				return;
			}
			foreach (string trophyPrefab in GetTrophyPrefabs())
			{
				TrophyRule? trophyRule = GetTrophyRule(trophyPrefab);
				if (!trophyRule.HasValue)
				{
					continue;
				}
				ConfigEntry<int> val = ((trophyRule.Value == TrophyRule.Boss) ? _bossCoalAmount : ((trophyRule.Value == TrophyRule.LargeCreature) ? _largeCreatureCoalAmount : _otherTrophyCoalAmount));
				if (val == null)
				{
					continue;
				}
				string text = "Trophy_" + trophyPrefab;
				if (!value.TryGetValue(text, out var value2))
				{
					ItemDrop val2 = FindItem(trophyPrefab);
					ItemDrop val3 = FindItem("Coal");
					if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val3 == (Object)null))
					{
						value2 = CreateConversion(text, conversions, (IReadOnlyList<ItemDrop>)(object)new ItemDrop[1] { val2 }, new int[1] { 1 }, new ConfigEntry<int>[1], val3, val);
						if (value2 != null)
						{
							value.Add(text, value2);
						}
					}
				}
				else
				{
					value2.OutputAmount = val;
					UpdateConversion(value2);
				}
			}
			foreach (TradeDefinition value3 in _trades.Values)
			{
				EnsureTradeConversion(value3, conversions, value);
			}
			SortConversions(conversions);
		}

		private void EnsureIdolConversions(Incinerator incinerator, Inventory inventory)
		{
			if (!_conversions.TryGetValue(incinerator, out Dictionary<string, ConversionState> value) || !(ConversionListField.GetValue(incinerator) is IList conversions))
			{
				return;
			}
			if (!_idolConversions.TryGetValue(incinerator, out Dictionary<string, IdolConversionBinding> value2))
			{
				value2 = new Dictionary<string, IdolConversionBinding>(StringComparer.OrdinalIgnoreCase);
				_idolConversions.Add(incinerator, value2);
			}
			Dictionary<string, (ItemDrop, IdolSacrificeKind)> dictionary = new Dictionary<string, (ItemDrop, IdolSacrificeKind)>(StringComparer.OrdinalIgnoreCase);
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				GameObject dropPrefab = allItem.m_dropPrefab;
				ItemDrop val = (((Object)(object)dropPrefab == (Object)null) ? null : dropPrefab.GetComponent<ItemDrop>());
				IdolSacrificeKind? idolSacrificeKind = (((Object)(object)val == (Object)null) ? ((IdolSacrificeKind?)null) : GetIdolSacrificeKind(val));
				if ((Object)(object)val != (Object)null && idolSacrificeKind.HasValue)
				{
					dictionary[((Object)dropPrefab).name] = (val, idolSacrificeKind.Value);
				}
			}
			foreach (KeyValuePair<string, (ItemDrop, IdolSacrificeKind)> item3 in dictionary)
			{
				string key = item3.Key;
				ItemDrop item = item3.Value.Item1;
				IdolSacrificeKind item2 = item3.Value.Item2;
				foreach (IdolSacrificeDefinition idolSacrifice in _idolSacrifices)
				{
					bool flag = item2 == IdolSacrificeKind.Battle;
					string text = (flag ? idolSacrifice.BattleIdolPrefab : idolSacrifice.ProtectionIdolPrefab);
					ConfigEntry<int> outputAmount = (flag ? idolSacrifice.BattleIdolAmount : idolSacrifice.ProtectionIdolAmount);
					string text2 = "Idol_" + idolSacrifice.Id + "_" + (flag ? "Battle_" : "Protection_") + key;
					ItemDrop val2 = FindItem(idolSacrifice.MaterialPrefab);
					ItemDrop val3 = FindItem(idolSacrifice.WoodPrefab);
					ItemDrop val4 = FindItem("SurtlingCore");
					ItemDrop val5 = FindItem(text);
					if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null || (Object)(object)val5 == (Object)null)
					{
						continue;
					}
					ItemDrop[] inputs = (ItemDrop[])(object)new ItemDrop[4] { val2, val3, val4, item };
					ConfigEntry<int>[] array = new ConfigEntry<int>[4] { idolSacrifice.MaterialAmount, idolSacrifice.WoodAmount, idolSacrifice.SurtlingCoreAmount, idolSacrifice.EquipmentAmount };
					if (!value.TryGetValue(text2, out var value3))
					{
						value3 = CreateConversion(text2, conversions, inputs, array.Select((ConfigEntry<int> setting) => setting.Value).ToArray(), array, val5, outputAmount);
						if (value3 != null)
						{
							value.Add(text2, value3);
							value2.Add(text2, new IdolConversionBinding(text2, value3, idolSacrifice, key, text, flag));
						}
						continue;
					}
					value3.Output = val5;
					value3.OutputAmount = outputAmount;
					for (int num = 0; num < array.Length; num++)
					{
						value3.InputSettings[num] = array[num];
						value3.InputAmounts[num] = array[num].Value;
					}
					UpdateConversion(value3);
				}
			}
			SortConversions(conversions);
		}

		private static IdolSacrificeKind? GetIdolSacrificeKind(ItemDrop item)
		{
			SharedData val = item.m_itemData?.m_shared;
			if (val == null)
			{
				return null;
			}
			switch (((object)Unsafe.As<ItemType, ItemType>(ref val.m_itemType)/*cast due to .constrained prefix*/).ToString())
			{
			case "OneHandedWeapon":
			case "Bow":
			case "TwoHandedWeapon":
			case "TwoHandedWeaponLeft":
			case "Attach_Atgeir":
				return IdolSacrificeKind.Battle;
			case "Shield":
			case "Helmet":
			case "Chest":
			case "Legs":
			case "Hands":
			case "Shoulder":
				return IdolSacrificeKind.Protection;
			default:
				return null;
			}
		}

		private IEnumerable<string> GetTrophyPrefabs()
		{
			if (!(ObjectDbItemsField.GetValue(ObjectDB.instance) is IList items))
			{
				yield break;
			}
			foreach (object item in items)
			{
				GameObject gameObject = (GameObject)((item is GameObject) ? item : null);
				if (gameObject != null)
				{
					string name = ((Object)gameObject).name;
					if (name.StartsWith("Trophy", StringComparison.OrdinalIgnoreCase) && !name.StartsWith("TrophyFish", StringComparison.OrdinalIgnoreCase))
					{
						yield return name;
					}
				}
			}
		}

		private TrophyRule? GetTrophyRule(string prefab)
		{
			if (prefab.Equals("TrophySurtling", StringComparison.OrdinalIgnoreCase) || prefab.Equals("TrophyBoar", StringComparison.OrdinalIgnoreCase) || prefab.Equals("TrophyDeer", StringComparison.OrdinalIgnoreCase) || prefab.Equals("TrophyHare", StringComparison.OrdinalIgnoreCase))
			{
				return null;
			}
			if (BossTrophies.Contains(prefab))
			{
				return TrophyRule.Boss;
			}
			if (LargeTrophies.Contains(prefab))
			{
				return TrophyRule.LargeCreature;
			}
			return TrophyRule.OtherCreature;
		}

		private void EnsureTradeConversion(TradeDefinition trade, IList conversions, Dictionary<string, ConversionState> states)
		{
			string[] array = trade.Inputs.Where((string input, int index) => input != null && trade.InputAmounts[index] != null).ToArray();
			if (array.Length == 0)
			{
				return;
			}
			if (!states.TryGetValue(trade.Id, out ConversionState value))
			{
				List<ItemDrop> list = new List<ItemDrop>();
				List<ConfigEntry<int>> list2 = new List<ConfigEntry<int>>();
				foreach (var item in trade.Inputs.Select((string input, int index) => new { input, index }))
				{
					if (item.input != null && trade.InputAmounts[item.index] != null)
					{
						ItemDrop val = FindItem(item.input);
						ConfigEntry<int> val2 = trade.InputAmounts[item.index];
						if ((Object)(object)val == (Object)null || val2 == null)
						{
							return;
						}
						list.Add(val);
						list2.Add(val2);
					}
				}
				ItemDrop val3 = FindItem(trade.OutputPrefab);
				if (!((Object)(object)val3 == (Object)null))
				{
					value = CreateConversion(trade.Id, conversions, list, list2.Select((ConfigEntry<int> x) => x.Value).ToArray(), list2.Cast<ConfigEntry<int>>().ToArray(), val3, trade.OutputAmount);
					if (value != null)
					{
						states.Add(trade.Id, value);
					}
				}
				return;
			}
			value.OutputAmount = trade.OutputAmount;
			for (int num = 0; num < value.InputAmounts.Count && num < trade.InputAmounts.Length; num++)
			{
				if (trade.InputAmounts[num] != null)
				{
					value.InputSettings[num] = trade.InputAmounts[num];
					value.InputAmounts[num] = trade.InputAmounts[num].Value;
				}
			}
			UpdateConversion(value);
		}

		private ConversionState? CreateConversion(string id, IList conversions, IReadOnlyList<ItemDrop> inputs, IReadOnlyList<int> inputAmounts, IReadOnlyList<ConfigEntry<int>?> inputSettings, ItemDrop output, ConfigEntry<int> outputAmount)
		{
			Type typeFromHandle = typeof(IncineratorConversion);
			object obj = Activator.CreateInstance(typeFromHandle, nonPublic: true);
			if (obj == null)
			{
				return null;
			}
			IList list = RequirementsField.GetValue(obj) as IList;
			if (list == null)
			{
				list = (IList)Activator.CreateInstance(RequirementsField.FieldType);
				RequirementsField.SetValue(obj, list);
			}
			ConversionState conversionState = new ConversionState(id, obj, output, outputAmount, inputs.Count);
			for (int i = 0; i < inputs.Count; i++)
			{
				object obj2 = Activator.CreateInstance(typeof(Requirement), nonPublic: true);
				if (obj2 == null)
				{
					return null;
				}
				RequirementItemField.SetValue(obj2, inputs[i]);
				RequirementAmountField.SetValue(obj2, inputAmounts[i]);
				list.Add(obj2);
				conversionState.RequirementObjects.Add(obj2);
				conversionState.InputAmounts.Add(inputAmounts[i]);
				conversionState.InputSettings.Add(inputSettings[i]);
			}
			conversions.Add(obj);
			UpdateConversion(conversionState);
			return conversionState;
		}

		private static void UpdateConversion(ConversionState state)
		{
			ResultField.SetValue(state.Conversion, state.Output);
			ResultAmountField.SetValue(state.Conversion, Math.Max(0, state.OutputAmount.Value));
			OnlyOneField.SetValue(state.Conversion, false);
			bool flag = true;
			for (int i = 0; i < state.RequirementObjects.Count; i++)
			{
				int num = state.InputSettings[i]?.Value ?? state.InputAmounts[i];
				if (num <= 0)
				{
					flag = false;
				}
				RequirementAmountField.SetValue(state.RequirementObjects[i], flag ? Math.Max(1, num) : int.MaxValue);
			}
			PriorityField.SetValue(state.Conversion, flag ? int.MaxValue : int.MinValue);
		}

		private ItemDrop? FindItem(string prefabName)
		{
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				return null;
			}
			try
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefabName);
				ItemDrop val = (((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent<ItemDrop>());
				if ((Object)(object)val == (Object)null && _loggedMissingItems.Add(prefabName))
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)(Text("Item prefab was not found: ", "No se encontró el prefab del objeto: ") + prefabName));
				}
				return val;
			}
			catch (Exception ex)
			{
				if (_loggedMissingItems.Add(prefabName))
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)(Text("Could not resolve item prefab ", "No se pudo localizar el prefab ") + prefabName + ": " + ex.Message));
				}
				return null;
			}
		}

		private void SortConversions(IList conversions)
		{
			if (conversions is List<IncineratorConversion> list)
			{
				list.Sort((IncineratorConversion a, IncineratorConversion b) => ((int)PriorityField.GetValue(b)).CompareTo((int)PriorityField.GetValue(a)));
			}
		}

		private bool OnRequestIncinerate(Incinerator incinerator, long playerId)
		{
			try
			{
				EnsureConversions(incinerator);
				object? value = ContainerField.GetValue(incinerator);
				Container val = (Container)((value is Container) ? value : null);
				Inventory val2 = ((val != null) ? val.GetInventory() : null);
				object? value2 = NetworkViewField.GetValue(incinerator);
				ZNetView val3 = (ZNetView)((value2 is ZNetView) ? value2 : null);
				if (val2 == null || (Object)(object)val3 == (Object)null || val.IsInUse() || (bool)(AccessTools.Field(typeof(Incinerator), "isInUse").GetValue(incinerator) ?? ((object)false)) || val2.NrOfItems() == 0)
				{
					return true;
				}
				EnsureIdolConversions(incinerator, val2);
				Player player = Player.GetPlayer(playerId);
				foreach (TradeDefinition item in _trades.Values.Where((TradeDefinition candidate) => candidate.Knowledge.HasValue))
				{
					if (!HasAllInputs(val2, item.Inputs, item.InputAmounts))
					{
						continue;
					}
					if ((Object)(object)player != (Object)null && KnowsPrerequisite(player, item))
					{
						ZDO zDO = val3.GetZDO();
						if (zDO != null)
						{
							zDO.Set(CreateCounterKey(playerId, item.Id), 0);
						}
						continue;
					}
					if (!_conversions.TryGetValue(incinerator, out Dictionary<string, ConversionState> value3) || !value3.TryGetValue(item.Id, out var value4))
					{
						SendResponse(val3, playerId, 40);
						return false;
					}
					if (!PrepareThorPunishment(incinerator, val3, playerId, item.Id, value4, item.PunishmentPrefab, item.PunishmentAmount))
					{
						return false;
					}
					return true;
				}
				if (_idolConversions.TryGetValue(incinerator, out Dictionary<string, IdolConversionBinding> value5))
				{
					foreach (IdolConversionBinding value6 in value5.Values)
					{
						IdolSacrificeDefinition sacrifice = value6.Sacrifice;
						string[] inputPrefabs = new string[4] { sacrifice.MaterialPrefab, sacrifice.WoodPrefab, "SurtlingCore", value6.GearPrefab };
						ConfigEntry<int>[] inputAmounts = new ConfigEntry<int>[4] { sacrifice.MaterialAmount, sacrifice.WoodAmount, sacrifice.SurtlingCoreAmount, sacrifice.EquipmentAmount };
						if (!HasAllInputs(val2, inputPrefabs, inputAmounts))
						{
							continue;
						}
						string text = FindItem(value6.IdolPrefab)?.m_itemData?.m_shared?.m_name;
						if ((Object)(object)player != (Object)null && !string.IsNullOrEmpty(text) && player.IsMaterialKnown(text))
						{
							ZDO zDO2 = val3.GetZDO();
							if (zDO2 != null)
							{
								zDO2.Set(CreateCounterKey(playerId, value6.CounterId), 0);
							}
							continue;
						}
						if (!PrepareThorPunishment(incinerator, val3, playerId, value6.CounterId, value6.Conversion, "Coal", _idolThorPunishmentAmount))
						{
							return false;
						}
						return true;
					}
				}
				return true;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)(Text("Failed to prepare Obliterator Tweaks offerings; vanilla activation will continue. ", "No se pudieron preparar las ofrendas de Obliterator Tweaks; continuará la activación vanilla. ") + ex));
				return true;
			}
		}

		private bool HasAllInputs(Inventory inventory, string?[] inputPrefabs, ConfigEntry<int>?[] inputAmounts)
		{
			for (int i = 0; i < inputPrefabs.Length; i++)
			{
				string text = inputPrefabs[i];
				int num = inputAmounts[i]?.Value ?? 0;
				if (string.IsNullOrEmpty(text) || num <= 0 || CountPrefabItems(inventory, text) < num)
				{
					return false;
				}
			}
			return true;
		}

		private static int CountPrefabItems(Inventory inventory, string prefabName)
		{
			int num = 0;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				GameObject dropPrefab = allItem.m_dropPrefab;
				if (string.Equals((dropPrefab != null) ? ((Object)dropPrefab).name : null, prefabName, StringComparison.OrdinalIgnoreCase))
				{
					num += allItem.m_stack;
				}
			}
			return num;
		}

		private bool PrepareThorPunishment(Incinerator incinerator, ZNetView view, long playerId, string counterId, ConversionState conversion, string punishmentPrefab, ConfigEntry<int>? punishmentAmount)
		{
			ZDO zDO = view.GetZDO();
			string text = CreateCounterKey(playerId, counterId);
			int num = ((zDO != null) ? zDO.GetInt(text, 0) : 0) + 1;
			if (zDO != null)
			{
				zDO.Set(text, num);
			}
			if (num < 5)
			{
				SendResponse(view, playerId, 40);
				return false;
			}
			ItemDrop val = FindItem(punishmentPrefab);
			if ((Object)(object)val == (Object)null || punishmentAmount == null)
			{
				SendResponse(view, playerId, 40);
				return false;
			}
			ItemDrop output = conversion.Output;
			ConfigEntry<int> outputAmount = conversion.OutputAmount;
			conversion.Output = val;
			conversion.OutputAmount = punishmentAmount;
			UpdateConversion(conversion);
			_pendingPunishments[incinerator] = new PendingPunishment(conversion, output, outputAmount);
			SendResponse(view, playerId, 41);
			return true;
		}

		private bool KnowsPrerequisite(Player player, TradeDefinition trade)
		{
			ItemDrop val = FindItem(trade.KnowledgePrefab);
			if ((Object)(object)val == (Object)null || val.m_itemData?.m_shared == null)
			{
				return false;
			}
			string name = val.m_itemData.m_shared.m_name;
			return (trade.Knowledge == KnowledgeKind.Material) ? player.IsMaterialKnown(name) : player.IsRecipeKnown(name);
		}

		private static string CreateCounterKey(long playerId, string tradeId)
		{
			string text = new string(tradeId.Where(char.IsLetterOrDigit).ToArray());
			return "ObliteratorTweaks.Attempts." + playerId.ToString(CultureInfo.InvariantCulture) + "." + text;
		}

		private static void SendResponse(ZNetView view, long targetPlayerId, int response)
		{
			view.InvokeRPC(targetPlayerId, "RPC_IncinerateRespons", new object[1] { response });
		}

		private IEnumerator RestorePunishmentAfterRun(IEnumerator routine, Incinerator incinerator, PendingPunishment punishment)
		{
			try
			{
				while (routine.MoveNext())
				{
					yield return routine.Current;
				}
			}
			finally
			{
				punishment.Conversion.Output = punishment.OriginalOutput;
				punishment.Conversion.OutputAmount = punishment.OriginalOutputAmount;
				UpdateConversion(punishment.Conversion);
				_pendingPunishments.Remove(incinerator);
			}
		}

		private bool HandleIncineratorResponse(int response)
		{
			switch (response)
			{
			case 40:
				ShowClientMessage(Text("te falta conocimiento para este sacrificio", "You lack the knowledge for this sacrifice."));
				return false;
			case 41:
				_clientThorMessagePending = true;
				return false;
			default:
				if (_clientThorMessagePending)
				{
					_clientThorMessagePending = false;
					ShowClientMessage(Text("has fastidiado a Thor", "You have angered Thor."));
					return false;
				}
				return true;
			}
		}

		private static void ShowClientMessage(string message)
		{
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, message, 0, (Sprite)null, false);
			}
		}

		internal static bool PatchRequest(Incinerator instance, long playerId)
		{
			return _instance?.OnRequestIncinerate(instance, playerId) ?? true;
		}

		internal static void PatchIncinerate(Incinerator instance, ref IEnumerator result)
		{
			if ((Object)(object)_instance != (Object)null && _instance._pendingPunishments.TryGetValue(instance, out PendingPunishment value))
			{
				result = _instance.RestorePunishmentAfterRun(result, instance, value);
			}
		}

		internal static bool PatchResponse(int response)
		{
			return _instance?.HandleIncineratorResponse(response) ?? true;
		}
	}
}