Decompiled source of JGFoP v0.1.0

BepInEx/plugins/JGFoP.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Extensions;
using Jotunn.Managers;
using Jotunn.Utils;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("JGFoP")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("JGFoP")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("0.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
namespace JGFoP;

internal static class FoPConfig
{
	internal static ConfigEntry<float> SuccessChance;

	internal static ConfigEntry<float> FailureChance;

	internal static ConfigEntry<float> BreakOnFailureChance;

	internal static ConfigEntry<int> LevelsLostOnFailure;

	internal static ConfigEntry<int> MinimumLevelOnFailure;

	internal static ConfigEntry<bool> FailureAtMinimumLevelBreaksItem;

	internal static ConfigEntry<bool> BreakReturnsItemAtLevel1;

	internal static ConfigEntry<float> BreakReturnIngredientsPercent;

	internal static ConfigEntry<int> MaxUpgradeLevel;

	internal static ConfigEntry<int> CostStart;

	internal static ConfigEntry<int> CostIncreasePerInterval;

	internal static ConfigEntry<int> CostIncreaseInterval;

	internal static ConfigEntry<int> CostScalingLevelStart;

	internal static ConfigEntry<float> UpgradeBaseDuration;

	internal static ConfigEntry<float> UpgradeDurationIncreasePerLevel;

	internal static void Bind(ConfigFile config)
	{
		AcceptableValueBase val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f);
		SuccessChance = ConfigFileExtensions.BindConfig<float>(config, "01 - Success and Failure", "Success Chance", 70f, "Relative chance that an upgrade attempt succeeds.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f);
		FailureChance = ConfigFileExtensions.BindConfig<float>(config, "01 - Success and Failure", "Failure Chance", 30f, "Relative chance that an upgrade attempt fails. Success and Failure are normalized as weights.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f);
		BreakOnFailureChance = ConfigFileExtensions.BindConfig<float>(config, "01 - Success and Failure", "Break On Failure Chance", 0f, "Percentage of failed upgrade attempts that break the item.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100);
		LevelsLostOnFailure = ConfigFileExtensions.BindConfig<int>(config, "02 - Failure Consequences", "Levels Lost On Failure", 1, "Number of upgrade levels lost after a normal failed attempt.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100);
		MinimumLevelOnFailure = ConfigFileExtensions.BindConfig<int>(config, "02 - Failure Consequences", "Minimum Level On Failure", 1, "Lowest upgrade level an item can be reduced to by normal failure.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		FailureAtMinimumLevelBreaksItem = ConfigFileExtensions.BindConfig<bool>(config, "02 - Failure Consequences", "Failure At Minimum Level Breaks Item", false, "If enabled, a failure while already at the minimum failure level becomes a break.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		BreakReturnsItemAtLevel1 = ConfigFileExtensions.BindConfig<bool>(config, "03 - Breaking", "Break Returns Item At Level 1", false, "If enabled, a broken item is returned at upgrade level 1 instead of being destroyed.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f);
		BreakReturnIngredientsPercent = ConfigFileExtensions.BindConfig<float>(config, "03 - Breaking", "Break Return Ingredients Percent", 0f, "Percentage of recoverable recipe ingredients returned when an item is destroyed by a break.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000);
		MaxUpgradeLevel = ConfigFileExtensions.BindConfig<int>(config, "04 - Upgrade Limits", "Max Upgrade Level", 100, "Maximum Forge of Potential upgrade level.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000);
		CostStart = ConfigFileExtensions.BindConfig<int>(config, "05 - Idol Cost", "Cost Start", 1, "Base number of idols required for an upgrade.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000);
		CostIncreasePerInterval = ConfigFileExtensions.BindConfig<int>(config, "05 - Idol Cost", "Cost Increase Per Interval", 1, "Number of additional idols required whenever a cost interval is reached.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000);
		CostIncreaseInterval = ConfigFileExtensions.BindConfig<int>(config, "05 - Idol Cost", "Cost Increase Interval", 2, "Number of upgrade levels between idol cost increases.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000);
		CostScalingLevelStart = ConfigFileExtensions.BindConfig<int>(config, "05 - Idol Cost", "Cost Scaling Level Start", 6, "Upgrade level at which idol cost scaling begins.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f);
		UpgradeBaseDuration = ConfigFileExtensions.BindConfig<float>(config, "06 - Upgrade Duration", "Base Crafting Time", 2f, "Base duration in seconds for a Forge of Potential upgrade.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f);
		UpgradeDurationIncreasePerLevel = ConfigFileExtensions.BindConfig<float>(config, "06 - Upgrade Duration", "Upgrade Duration Increase Per Level", 0f, "Additional crafting time in seconds added for each upgrade level.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
	}
}
internal static class ForgePotentialPatches
{
	private sealed class UpgradeAttemptState
	{
		internal string ItemPrefabName;

		internal int StartingQuality;

		internal ItemData OriginalItem;

		internal int Variant;

		internal long PlayerID;

		internal string PlayerName;

		internal bool ItemGivenBack;

		internal int QualityGivenBack;
	}

	[HarmonyPatch(typeof(InventoryGui), "SetupCrafting")]
	internal static class UpgradeDurationPatch
	{
		[HarmonyPrefix]
		private static void Prefix(ref float ___m_upgraderDuration, ref float ___m_upgraderDurationPerLevel)
		{
			___m_upgraderDuration = Math.Max(0f, FoPConfig.UpgradeBaseDuration.Value);
			___m_upgraderDurationPerLevel = Math.Max(0f, FoPConfig.UpgradeDurationIncreasePerLevel.Value);
		}
	}

	[HarmonyPatch(typeof(Requirement), "GetAmount")]
	internal static class UpgraderCostPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Requirement __instance, int qualityLevel, ref int __result)
		{
			if (__instance != null && __instance.m_upgraderResource)
			{
				int num = qualityLevel - 1;
				int num2 = Math.Max(1, FoPConfig.CostStart.Value);
				int num3 = Math.Max(1, FoPConfig.CostScalingLevelStart.Value);
				int num4 = Math.Max(1, FoPConfig.CostIncreaseInterval.Value);
				int num5 = Math.Max(0, FoPConfig.CostIncreasePerInterval.Value);
				int num6 = num2;
				if (num >= num3)
				{
					int num7 = (num - num3) / num4 + 1;
					num6 += num7 * num5;
				}
				__result = Math.Max(1, num6);
			}
		}
	}

	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	internal static class MaxUpgradeLevelPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(Player player, ItemData ___m_craftUpgradeItem)
		{
			if ((Object)(object)player == (Object)null || ___m_craftUpgradeItem == null)
			{
				return true;
			}
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			if ((Object)(object)currentCraftingStation == (Object)null || !currentCraftingStation.m_upgrader)
			{
				return true;
			}
			int num = Math.Max(1, FoPConfig.MaxUpgradeLevel.Value);
			if (___m_craftUpgradeItem.m_quality < num)
			{
				return true;
			}
			MessageHud instance = MessageHud.instance;
			if (instance != null)
			{
				instance.ShowMessage((MessageType)2, $"Maximum upgrade level reached: {num}", 0, (Sprite)null, false, true);
			}
			return false;
		}
	}

	private sealed class SavedUpgradeChances
	{
		internal SharedData Shared;

		internal float UpgradeChance;

		internal float BreakChance;

		internal float BreakReturn;
	}

	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	internal static class UpgradeChancePatch
	{
		[HarmonyPrefix]
		private static void Prefix(Player player, Recipe ___m_craftRecipe, ItemData ___m_craftUpgradeItem, ref List<SavedUpgradeChances> __state)
		{
			__state = null;
			if ((Object)(object)player == (Object)null || ___m_craftUpgradeItem == null || (Object)(object)___m_craftRecipe == (Object)null)
			{
				return;
			}
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			if ((Object)(object)currentCraftingStation == (Object)null || !currentCraftingStation.m_upgrader)
			{
				return;
			}
			int num = Math.Max(1, FoPConfig.MaxUpgradeLevel.Value);
			if (___m_craftUpgradeItem.m_quality >= num || ___m_craftRecipe.m_resources == null)
			{
				return;
			}
			float num2 = Math.Max(0f, FoPConfig.SuccessChance.Value);
			float num3 = Math.Max(0f, FoPConfig.FailureChance.Value);
			float num4 = num2 + num3;
			float num5 = ((num4 > 0f) ? (num2 / num4) : 0f);
			float num6 = Math.Max(0f, Math.Min(100f, FoPConfig.BreakOnFailureChance.Value)) / 100f;
			float breakChance = (1f - num5) * num6;
			int num7 = Math.Max(1, FoPConfig.MinimumLevelOnFailure.Value);
			if (FoPConfig.FailureAtMinimumLevelBreaksItem.Value && ___m_craftUpgradeItem.m_quality <= num7)
			{
				breakChance = 1f - num5;
			}
			List<SavedUpgradeChances> list = new List<SavedUpgradeChances>();
			Requirement[] resources = ___m_craftRecipe.m_resources;
			foreach (Requirement val in resources)
			{
				if (val == null || !val.m_upgraderResource || (Object)(object)val.m_resItem == (Object)null)
				{
					continue;
				}
				ItemData itemData = val.m_resItem.m_itemData;
				if (itemData != null && itemData.m_shared != null)
				{
					SharedData shared = itemData.m_shared;
					list.Add(new SavedUpgradeChances
					{
						Shared = shared,
						UpgradeChance = shared.m_upgradeChance,
						BreakChance = shared.m_breakChance,
						BreakReturn = shared.m_breakReturnIngreientsAmount
					});
					shared.m_upgradeChance = num5;
					shared.m_breakChance = breakChance;
					if (FoPConfig.BreakReturnsItemAtLevel1.Value)
					{
						shared.m_breakReturnIngreientsAmount = 0f;
					}
					else
					{
						shared.m_breakReturnIngreientsAmount = Math.Max(0f, Math.Min(100f, FoPConfig.BreakReturnIngredientsPercent.Value)) / 100f;
					}
				}
			}
			if (list.Count > 0)
			{
				__state = list;
			}
		}

		[HarmonyPostfix]
		private static void Postfix(List<SavedUpgradeChances> __state)
		{
			if (__state == null)
			{
				return;
			}
			foreach (SavedUpgradeChances item in __state)
			{
				if (item?.Shared != null)
				{
					item.Shared.m_upgradeChance = item.UpgradeChance;
					item.Shared.m_breakChance = item.BreakChance;
					item.Shared.m_breakReturnIngreientsAmount = item.BreakReturn;
				}
			}
		}
	}

	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	internal static class FailureLevelContextPatch
	{
		[HarmonyPrefix]
		private static void Prefix(Player player, Recipe ___m_craftRecipe, ItemData ___m_craftUpgradeItem)
		{
			_activeUpgrade = null;
			if (!((Object)(object)player == (Object)null) && !((Object)(object)___m_craftRecipe == (Object)null) && ___m_craftUpgradeItem != null && !((Object)(object)___m_craftRecipe.m_item == (Object)null))
			{
				CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
				if (!((Object)(object)currentCraftingStation == (Object)null) && currentCraftingStation.m_upgrader)
				{
					_activeUpgrade = new UpgradeAttemptState
					{
						ItemPrefabName = ((Object)((Component)___m_craftRecipe.m_item).gameObject).name,
						StartingQuality = ___m_craftUpgradeItem.m_quality,
						OriginalItem = ___m_craftUpgradeItem,
						Variant = ___m_craftUpgradeItem.m_variant,
						PlayerID = player.GetPlayerID(),
						PlayerName = player.GetPlayerName(),
						ItemGivenBack = false,
						QualityGivenBack = 0
					};
				}
			}
		}

		[HarmonyPostfix]
		private static void Postfix(Player player)
		{
			UpgradeAttemptState activeUpgrade = _activeUpgrade;
			_activeUpgrade = null;
			if (activeUpgrade != null && !((Object)(object)player == (Object)null))
			{
				Inventory inventory = ((Humanoid)player).GetInventory();
				if (inventory != null && (activeUpgrade.OriginalItem == null || !inventory.ContainsItem(activeUpgrade.OriginalItem)) && !activeUpgrade.ItemGivenBack && FoPConfig.BreakReturnsItemAtLevel1.Value && inventory.AddItem(activeUpgrade.ItemPrefabName, 1, 1, activeUpgrade.Variant, activeUpgrade.PlayerID, activeUpgrade.PlayerName ?? "", false, false) == null)
				{
					Logger.LogWarning((object)("JGFoP could not return " + activeUpgrade.ItemPrefabName + " at level 1 after a break."));
				}
			}
		}
	}

	[HarmonyPatch]
	internal static class FailedUpgradeQualityPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo[] methods = typeof(Inventory).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.Name != "AddItem" || methodInfo.ReturnType != typeof(ItemData))
				{
					continue;
				}
				ParameterInfo[] parameters = methodInfo.GetParameters();
				bool flag = false;
				bool flag2 = false;
				ParameterInfo[] array = parameters;
				foreach (ParameterInfo parameterInfo in array)
				{
					if (parameterInfo.ParameterType == typeof(string) && parameterInfo.Name == "name")
					{
						flag = true;
					}
					if (parameterInfo.ParameterType == typeof(int) && parameterInfo.Name == "quality")
					{
						flag2 = true;
					}
				}
				if (flag && flag2)
				{
					yield return methodInfo;
				}
			}
		}

		[HarmonyPrefix]
		private static void Prefix(string name, ref int quality)
		{
			if (_activeUpgrade == null || !string.Equals(name, _activeUpgrade.ItemPrefabName, StringComparison.Ordinal))
			{
				return;
			}
			if (quality > _activeUpgrade.StartingQuality)
			{
				_activeUpgrade.ItemGivenBack = true;
				_activeUpgrade.QualityGivenBack = quality;
				return;
			}
			int num = Math.Max(0, FoPConfig.LevelsLostOnFailure.Value);
			int num2 = Math.Max(1, FoPConfig.MinimumLevelOnFailure.Value);
			int num3 = _activeUpgrade.StartingQuality - num;
			if (num3 < num2)
			{
				num3 = num2;
			}
			if (num3 > _activeUpgrade.StartingQuality)
			{
				num3 = _activeUpgrade.StartingQuality;
			}
			quality = num3;
			_activeUpgrade.ItemGivenBack = true;
			_activeUpgrade.QualityGivenBack = quality;
		}
	}

	private static UpgradeAttemptState _activeUpgrade;
}
[BepInPlugin("com.jgmods.jgfop", "JGFoP", "0.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
internal class JGFoP : BaseUnityPlugin
{
	public const string PluginGUID = "com.jgmods.jgfop";

	public const string PluginName = "JGFoP";

	public const string PluginVersion = "0.1.0";

	public static CustomLocalization Localization = LocalizationManager.Instance.GetLocalization();

	private Harmony _harmony;

	private void Awake()
	{
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Expected O, but got Unknown
		FoPConfig.Bind(((BaseUnityPlugin)this).Config);
		IdolRecipes.Register();
		CustomLocalization localization = Localization;
		string text = "English";
		string text2 = "msg_upgrader_failed";
		localization.AddTranslation(ref text, ref text2, "Refinement failed.");
		_harmony = new Harmony("com.jgmods.jgfop");
		_harmony.PatchAll();
		Logger.LogInfo((object)"JGFoP has landed");
	}

	private void OnDestroy()
	{
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}
}
internal static class IdolRecipes
{
	internal static void Register()
	{
		AddIdolPair(0, new(string, int)[4]
		{
			("FineWood", 20),
			("Tin", 10),
			("GreydwarfEye", 10),
			("TrophyEikthyr", 1)
		});
		AddIdolPair(1, new(string, int)[3]
		{
			("Bronze", 10),
			("SurtlingCore", 1),
			("TrophyTheElder", 1)
		});
		AddIdolPair(2, new(string, int)[3]
		{
			("Iron", 10),
			("ElderBark", 5),
			("TrophyBonemass", 1)
		});
		AddIdolPair(3, new(string, int)[4]
		{
			("Silver", 10),
			("FreezeGland", 5),
			("Obsidian", 5),
			("TrophyDragonQueen", 1)
		});
		AddIdolPair(4, new(string, int)[4]
		{
			("BlackMetal", 10),
			("Needle", 2),
			("Tar", 5),
			("TrophyGoblinKing", 1)
		});
		AddIdolPair(5, new(string, int)[4]
		{
			("BlackMarble", 20),
			("BugMeat", 10),
			("Carapace", 10),
			("TrophySeekerQueen", 1)
		});
		AddIdolPair(6, new(string, int)[3]
		{
			("FlametalNew", 10),
			("CharredBone", 15),
			("TrophyFader", 1)
		});
		AddIdolPair(7, new(string, int)[3]
		{
			("Gold", 10),
			("Coins", 40),
			("FrozenKingDrop", 1)
		});
		Logger.LogInfo((object)"Registered Forge recipes for all Forge of Potential idols.");
	}

	private static void AddIdolPair(int tier, (string Item, int Amount)[] requirements)
	{
		AddIdolRecipe($"Upgrader{tier}Armor", $"JGFoP_Upgrader{tier}Armor", requirements);
		AddIdolRecipe($"Upgrader{tier}Weapon", $"JGFoP_Upgrader{tier}Weapon", requirements);
	}

	private static void AddIdolRecipe(string idolPrefab, string recipeName, (string Item, int Amount)[] requirements)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Expected O, but got Unknown
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Expected O, but got Unknown
		RecipeConfig val = new RecipeConfig
		{
			Name = recipeName,
			Item = idolPrefab,
			Amount = 1,
			CraftingStation = "forge",
			MinStationLevel = 1
		};
		for (int i = 0; i < requirements.Length; i++)
		{
			var (text, num) = requirements[i];
			val.AddRequirement(text, num, 0);
		}
		ItemManager.Instance.AddRecipe(new CustomRecipe(val));
	}
}