Decompiled source of CraftingXP v1.6.2

CraftingXP_v1.6.2.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("CraftingXP")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.6.2.0")]
[assembly: AssemblyInformationalVersion("1.6.2")]
[assembly: AssemblyProduct("CraftingXP")]
[assembly: AssemblyTitle("CraftingXP")]
[assembly: AssemblyVersion("1.6.2.0")]
namespace CraftingXP;

[BepInPlugin("frost.craftingxp", "CraftingXP", "1.6.2")]
public class CraftingXPPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "frost.craftingxp";

	public const string PluginName = "CraftingXP";

	public const string PluginVersion = "1.6.2";

	internal static ManualLogSource Log;

	internal static ConfigEntry<float> GlobalMultiplier;

	internal static ConfigEntry<float> BuildMultiplier;

	internal static ConfigEntry<float> BonusPerPowerPoint;

	internal static ConfigEntry<float> MaxXP;

	internal static ConfigEntry<int> RemoveGrace;

	internal static ConfigEntry<float> BonusPerBarTier;

	internal static ConfigEntry<string> BarTiersConfig;

	private void Awake()
	{
		//IL_0140: Unknown result type (might be due to invalid IL or missing references)
		Log = ((BaseUnityPlugin)this).Logger;
		GlobalMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("CraftingXP", "GlobalMultiplier", 2f, "Во сколько раз усиливается весь опыт Ремесла (крафт, постройка, ремонт)");
		BuildMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("CraftingXP", "BuildMultiplier", 8f, "Дополнительный множитель опыта ТОЛЬКО за постановку деталей молотом, применяется поверх GlobalMultiplier: ваниль 0.25 × Global 2 × Build 8 = 4 XP за деталь. На крафт и ремонт не действует. Потолок MaxXP действует и здесь — если хочешь больше 5 XP за деталь, подними и MaxXP");
		BonusPerPowerPoint = ((BaseUnityPlugin)this).Config.Bind<float>("CraftingXP", "BonusPerPowerPoint", 0.02f, "Бонусный опыт за каждую единицу силы крафтимой вещи (урон, броня или блок)");
		MaxXP = ((BaseUnityPlugin)this).Config.Bind<float>("CraftingXP", "MaxXP", 5f, "Максимум опыта за одно действие");
		RemoveGrace = ((BaseUnityPlugin)this).Config.Bind<int>("CraftingXP", "RemoveGrace", 3, "Сколько построек подряд можно снести молотом без потери опыта (долг за снос не больше этого числа прощается; дальше долг = числу снесённого)");
		BonusPerBarTier = ((BaseUnityPlugin)this).Config.Bind<float>("CraftingXP", "BonusPerBarTier", 1f, "Опыт за одну заложенную руду = тир металла × это число (тиры в BarTiers). В ванили плавка не даёт опыта вообще. Потолок MaxXP на плавку не действует — значение ограничено только старшим тиром");
		BarTiersConfig = ((BaseUnityPlugin)this).Config.Bind<string>("CraftingXP", "BarTiers", "Copper:1,Tin:1,Bronze:2,Iron:3,Silver:4,BlackMetal:5,EitrInfusedIron:6,Flametal:7", "Тиры слитков: ИмяПрефаба:тир, через запятую. Сравнение по полному имени, регистр не важен. Если у тебя другое название слитка эйтр-стали — посмотри его в LogOutput.log (строка 'Слиток без тира') и впиши сюда");
		BarTiers.Parse(BarTiersConfig.Value);
		BarTiersConfig.SettingChanged += delegate
		{
			BarTiers.Parse(BarTiersConfig.Value);
		};
		new Harmony("frost.craftingxp").PatchAll();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"CraftingXP 1.6.2 загружен");
	}
}
internal static class CraftContext
{
	public const float None = -1f;

	public static float Power = -1f;
}
internal static class BuildContext
{
	public static bool Pending;
}
internal static class SmeltContext
{
	public const float None = -1f;

	public static float XP = -1f;
}
internal static class BarTiers
{
	private static readonly List<KeyValuePair<string, int>> _tiers = new List<KeyValuePair<string, int>>();

	private static readonly HashSet<string> _reportedUnknown = new HashSet<string>();

	public static void Parse(string config)
	{
		_tiers.Clear();
		string[] array = config.Split(',');
		for (int i = 0; i < array.Length; i++)
		{
			string[] array2 = array[i].Split(':');
			if (array2.Length == 2 && int.TryParse(array2[1].Trim(), out var result) && array2[0].Trim().Length > 0)
			{
				_tiers.Add(new KeyValuePair<string, int>(array2[0].Trim(), result));
			}
		}
		CraftingXPPlugin.Log.LogInfo((object)("Тиры слитков: " + config));
	}

	public static int GetTier(string prefabName, string sharedName)
	{
		string text = ((prefabName != null) ? prefabName.Replace("(Clone)", "") : "");
		string b = ((sharedName != null && sharedName.StartsWith("$item_")) ? sharedName.Substring(6) : (sharedName ?? ""));
		foreach (KeyValuePair<string, int> tier in _tiers)
		{
			if (string.Equals(tier.Key, text, StringComparison.OrdinalIgnoreCase) || string.Equals(tier.Key, b, StringComparison.OrdinalIgnoreCase))
			{
				return tier.Value;
			}
		}
		ReportUnknown(text);
		return 0;
	}

	private static void ReportUnknown(string prefab)
	{
		if (!string.IsNullOrEmpty(prefab))
		{
			string text = prefab.ToLowerInvariant();
			if ((text.Contains("metal") || text.Contains("eitr") || text.Contains("flame") || text.Contains("copper") || text.Contains("tin") || text.Contains("bronze") || text.Contains("iron") || text.Contains("silver")) && _reportedUnknown.Add(text))
			{
				CraftingXPPlugin.Log.LogInfo((object)("Слиток без тира: " + prefab + " — можно добавить в BarTiers в конфиге"));
			}
		}
	}
}
[HarmonyPatch(typeof(Smelter), "OnAddOre")]
internal static class Smelter_OnAddOre_Patch
{
	private static readonly FieldInfo ConversionField = AccessTools.Field(typeof(Smelter), "m_conversion");

	private static void Postfix(Smelter __instance, Humanoid user, ItemData item, bool __result)
	{
		if (!__result)
		{
			return;
		}
		Player val = (Player)(object)((user is Player) ? user : null);
		if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
		{
			return;
		}
		ItemConversion val2 = FindConversion(__instance, user, item);
		if (val2 == null)
		{
			return;
		}
		ItemDrop to = val2.m_to;
		if (!((Object)(object)to == (Object)null) && to.m_itemData != null && to.m_itemData.m_shared != null)
		{
			int tier = BarTiers.GetTier(((Object)((Component)to).gameObject).name, to.m_itemData.m_shared.m_name);
			if (tier > 0)
			{
				float num = (SmeltContext.XP = (float)tier * CraftingXPPlugin.BonusPerBarTier.Value);
				((Character)val).RaiseSkill((SkillType)107, num);
				CraftingXPPlugin.Log.LogInfo((object)string.Format("Руда в печь ({0} → {1}): +{2:F2} XP (тир {3})", (item != null && (Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : "?", ((Object)((Component)to).gameObject).name, num, tier));
			}
		}
	}

	private static ItemConversion FindConversion(Smelter smelter, Humanoid user, ItemData item)
	{
		if (!(ConversionField?.GetValue(smelter) is IEnumerable<ItemConversion> enumerable))
		{
			return null;
		}
		string text = ((item != null && (Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null);
		foreach (ItemConversion item2 in enumerable)
		{
			if (item2 == null || (Object)(object)item2.m_from == (Object)null || (Object)(object)item2.m_to == (Object)null)
			{
				continue;
			}
			if (text != null)
			{
				if (((Object)((Component)item2.m_from).gameObject).name == text)
				{
					return item2;
				}
			}
			else if (user.GetInventory() != null && user.GetInventory().GetItem(item2.m_from.m_itemData.m_shared.m_name, -1, false) != null)
			{
				return item2;
			}
		}
		return null;
	}
}
[HarmonyPatch(typeof(Player), "RaiseSkill")]
internal static class Player_RaiseSkill_Patch
{
	private static float _nextLogTime;

	private static void Prefix(Player __instance, SkillType skill, ref float value)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Invalid comparison between Unknown and I4
		if ((int)skill != 107 || value <= 0f)
		{
			return;
		}
		float xP = SmeltContext.XP;
		SmeltContext.XP = -1f;
		if (xP > 0f)
		{
			value = xP;
			return;
		}
		bool pending = BuildContext.Pending;
		BuildContext.Pending = false;
		if (pending)
		{
			value = Mathf.Min(value * CraftingXPPlugin.GlobalMultiplier.Value * CraftingXPPlugin.BuildMultiplier.Value, CraftingXPPlugin.MaxXP.Value);
			if (Time.time >= _nextLogTime)
			{
				_nextLogTime = Time.time + 10f;
				CraftingXPPlugin.Log.LogInfo((object)$"XP за деталь: {value:F2}");
			}
			return;
		}
		float num = ((CraftContext.Power > 0f) ? CraftContext.Power : 0f);
		CraftContext.Power = -1f;
		value = Mathf.Min(value * CraftingXPPlugin.GlobalMultiplier.Value + num * CraftingXPPlugin.BonusPerPowerPoint.Value, CraftingXPPlugin.MaxXP.Value);
		if (Time.time >= _nextLogTime)
		{
			_nextLogTime = Time.time + 10f;
			CraftingXPPlugin.Log.LogInfo((object)$"Crafting XP: {value:F2} (сила вещи: {num:F0})");
		}
	}
}
[HarmonyPatch(typeof(Player), "TryPlacePiece")]
internal static class Player_TryPlacePiece_Patch
{
	private static void Postfix(bool __result)
	{
		if (__result)
		{
			BuildContext.Pending = true;
		}
	}
}
[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
internal static class InventoryGui_DoCrafting_Patch
{
	private static void Prefix(InventoryGui __instance)
	{
		try
		{
			object? obj = AccessTools.Field(typeof(InventoryGui), "m_craftRecipe")?.GetValue(__instance);
			CraftContext.Power = CraftMath.CraftPower(((Recipe)(((obj is Recipe) ? obj : null)?)).m_item?.m_itemData?.m_shared);
		}
		catch (Exception ex)
		{
			Debug.LogError((object)("[CraftingXP] Ошибка расчёта силы вещи: " + ex));
		}
	}
}
[HarmonyPatch(typeof(Player), "UpdatePlacement")]
internal static class Player_UpdatePlacement_Patch
{
	private static readonly FieldInfo DebtField = AccessTools.Field(typeof(Player), "m_buildRemoveDebt");

	private static int _debtBefore;

	private static void Prefix(Player __instance)
	{
		BuildContext.Pending = false;
		_debtBefore = ((DebtField != null) ? ((int)DebtField.GetValue(__instance)) : 0);
	}

	private static void Postfix(Player __instance)
	{
		BuildContext.Pending = false;
		if (DebtField == null)
		{
			return;
		}
		int num = (int)DebtField.GetValue(__instance);
		if (num <= _debtBefore)
		{
			if (_debtBefore > 0 && num == 0)
			{
				((Character)__instance).Message((MessageType)2, "Штраф за снос снят — опыт за постройки вернулся", 0, (Sprite)null, false);
				CraftingXPPlugin.Log.LogInfo((object)"Долг за снос полностью выплачен");
			}
		}
		else if (num <= CraftingXPPlugin.RemoveGrace.Value)
		{
			DebtField.SetValue(__instance, 0);
			CraftingXPPlugin.Log.LogInfo((object)$"Снос прощён (льгота {CraftingXPPlugin.RemoveGrace.Value}): долг {num} → 0");
		}
		else
		{
			if (_debtBefore <= CraftingXPPlugin.RemoveGrace.Value)
			{
				((Character)__instance).Message((MessageType)2, $"Штраф за снос: следующие {num} построек без опыта Ремесла", 0, (Sprite)null, false);
			}
			CraftingXPPlugin.Log.LogInfo((object)string.Format("Долг за снос: {0} (следующие {0} построек без XP)", num));
		}
	}
}
internal static class CraftMath
{
	public static float CraftPower(SharedData shared)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		if (shared == null)
		{
			return 0f;
		}
		DamageTypes damages = shared.m_damages;
		return Mathf.Max(damages.m_damage + damages.m_blunt + damages.m_slash + damages.m_pierce + damages.m_fire + damages.m_frost + damages.m_lightning + damages.m_poison + damages.m_spirit + damages.m_chop + damages.m_pickaxe, Mathf.Max(shared.m_armor, shared.m_blockPower));
	}
}