Decompiled source of LagFix v1.0.0

LagFix.dll

Decompiled 17 hours ago
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using RoR2;
using RoR2.Orbs;
using RoR2.Projectile;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace LagFix;

[BepInPlugin("com.proigryfedorov.lagfix", "LagFix", "1.0.0")]
public class LagFixPlugin : BaseUnityPlugin
{
	private class ProjectileBudget
	{
		public float windowStart;

		public int fired;

		public float bankedDamage;
	}

	private class EffectBudget
	{
		public float windowStart;

		public int shown;
	}

	public const string PluginGuid = "com.proigryfedorov.lagfix";

	public const string PluginName = "LagFix";

	public const string PluginVersion = "1.0.0";

	internal static LagFixPlugin Instance;

	internal static ConfigEntry<bool> FastLuck;

	internal static ConfigEntry<bool> MergeThornStacks;

	internal static ConfigEntry<int> ThornStackCap;

	internal static ConfigEntry<bool> MergeProjectiles;

	internal static ConfigEntry<int> ProjectilesPerSecond;

	internal static ConfigEntry<string> MergedProjectiles;

	internal static ConfigEntry<bool> ThrottleEffects;

	internal static ConfigEntry<int> EffectsPerSecond;

	internal static ConfigEntry<bool> InstantExperience;

	internal static ConfigEntry<bool> InstantGold;

	internal static ConfigEntry<bool> TameBlackHoles;

	internal static ConfigEntry<string> BlackHoleNames;

	internal static ConfigEntry<bool> LogEffectNames;

	private static readonly Dictionary<string, ProjectileBudget> budgets = new Dictionary<string, ProjectileBudget>();

	private static string mergedListSource;

	private static string[] mergedList = new string[0];

	private static readonly Dictionary<int, EffectBudget> effectBudgets = new Dictionary<int, EffectBudget>();

	private void Awake()
	{
		//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bc: Expected O, but got Unknown
		Instance = this;
		FastLuck = ((BaseUnityPlugin)this).Config.Bind<bool>("Items", "FastLuck", true, "57-листный клевер: считать удачу по формуле одним-двумя бросками вместо броска на каждую единицу удачи. Шансы те же.");
		MergeThornStacks = ((BaseUnityPlugin)this).Config.Bind<bool>("Items", "MergeThornStacks", true, "Вредный шип: объединять переносимые стаки урона (кровотечение, горение…) в меньшее число стаков с тем же общим уроном.");
		ThornStackCap = ((BaseUnityPlugin)this).Config.Bind<int>("Items", "ThornStackCap", 50, "Вредный шип: сколько стаков одного эффекта максимум переносится одному врагу. Урон с лишних добавляется к ним; у дебаффов без урона лишние отбрасываются.");
		MergeProjectiles = ((BaseUnityPlugin)this).Config.Bind<bool>("Items", "MergeProjectiles", true, "AtG, Церемониальный кинжал, Связка петард: если снарядов больше лимита в секунду, лишние не создаются, а их урон добавляется к следующему снаряду.");
		ProjectilesPerSecond = ((BaseUnityPlugin)this).Config.Bind<int>("Items", "ProjectilesPerSecond", 12, "Сколько таких снарядов в секунду может выпустить один игрок (на каждый вид снаряда).");
		MergedProjectiles = ((BaseUnityPlugin)this).Config.Bind<string>("Items", "MergedProjectiles", "MissileProjectile,DaggerProjectile,FireworkProjectile", "Имена снарядов, которые объединяются (через запятую, достаточно части имени).");
		ThrottleEffects = ((BaseUnityPlugin)this).Config.Bind<bool>("Effects", "ThrottleEffects", true, "Ограничивать число одинаковых визуальных эффектов в секунду (взрывы Бензина, молнии Укулеле и Полилютни, Огонёк, Руническая линза…). На урон не влияет.");
		EffectsPerSecond = ((BaseUnityPlugin)this).Config.Bind<int>("Effects", "EffectsPerSecond", 15, "Сколько одинаковых эффектов в секунду показывать.");
		InstantExperience = ((BaseUnityPlugin)this).Config.Bind<bool>("Effects", "InstantExperience", true, "Давать опыт сразу, без летящих шариков опыта (у хоста).");
		InstantGold = ((BaseUnityPlugin)this).Config.Bind<bool>("Effects", "InstantGold", true, "Давать золото сразу, без летящих монеток (у хоста).");
		TameBlackHoles = ((BaseUnityPlugin)this).Config.Bind<bool>("Effects", "TameBlackHoles", true, "«Чёрные дыры»: убирать слепящие вспышки, свет и искажение, оставляя кольцо и границу.");
		BlackHoleNames = ((BaseUnityPlugin)this).Config.Bind<string>("Effects", "BlackHoleNames", "ElementalRingVoidBlackHole,NullifierDeathBomb,VoidJailerDeathBomb,VoidMegaCrabDeathBomb,VoidDeathBomb,GravSphere", "Части имён объектов, считающихся «чёрными дырами» (через запятую).");
		LogEffectNames = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogEffectNames", false, "Писать в лог имена появляющихся эффектов и снарядов — чтобы найти, какой эффект добавить в BlackHoleNames.");
		Harmony harmony = new Harmony("com.proigryfedorov.lagfix");
		Patch(harmony, typeof(Util), "CheckRoll", new Type[3]
		{
			typeof(float),
			typeof(float),
			typeof(CharacterMaster)
		}, "CheckRollPrefix");
		Patch(harmony, typeof(VineOrb), "OnArrival", Type.EmptyTypes, "VineOrbPrefix");
		Patch(harmony, typeof(ProjectileManager), "FireProjectile", new Type[1] { typeof(FireProjectileInfo) }, "FireProjectilePrefix");
		Patch(harmony, typeof(EffectManager), "SpawnEffect", new Type[3]
		{
			typeof(GameObject),
			typeof(EffectData),
			typeof(bool)
		}, "SpawnEffectPrefabPrefix");
		Patch(harmony, typeof(EffectManager), "SpawnEffect", new Type[3]
		{
			typeof(EffectIndex),
			typeof(EffectData),
			typeof(bool)
		}, "SpawnEffectIndexPrefix");
		Patch(harmony, typeof(ExperienceManager), "AwardExperience", null, "AwardExperiencePrefix");
		Patch(harmony, typeof(OrbManager), "AddOrb", null, "AddOrbPrefix");
		BlackHole.Patch(harmony);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"LagFix 1.0.0 loaded");
	}

	private void Patch(Harmony harmony, Type type, string method, Type[] args, string prefix)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Expected O, but got Unknown
		try
		{
			harmony.Patch((MethodBase)AccessTools.Method(type, method, args, (Type[])null), new HarmonyMethod(typeof(LagFixPlugin), prefix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Не удалось пропатчить " + type.Name + "." + method + ": " + ex.Message));
		}
	}

	internal static void Log(string text)
	{
		if ((Object)(object)Instance != (Object)null)
		{
			((BaseUnityPlugin)Instance).Logger.LogInfo((object)text);
		}
	}

	private static bool CheckRollPrefix(float percentChance, float luck, CharacterMaster effectOriginMaster, ref bool __result)
	{
		if (!FastLuck.Value)
		{
			return true;
		}
		if (percentChance <= 0f)
		{
			__result = false;
			return false;
		}
		int num = Mathf.CeilToInt(Mathf.Abs(luck));
		float num2 = Random.Range(0f, 100f);
		if (num == 0 || luck == 0f)
		{
			__result = num2 < percentChance;
			return false;
		}
		double num3 = (double)Math.Min(percentChance, 100f) / 100.0;
		if (luck > 0f)
		{
			if (num2 < percentChance)
			{
				__result = true;
				return false;
			}
			double num4 = 1.0 - Math.Pow(1.0 - num3, num);
			__result = (double)Random.value < num4;
			if (__result)
			{
				MarkLucky(effectOriginMaster);
			}
		}
		else
		{
			__result = num2 < percentChance && (double)Random.value < Math.Pow(num3, num);
		}
		return false;
	}

	private static void MarkLucky(CharacterMaster master)
	{
		if (!((Object)(object)master == (Object)null))
		{
			GameObject bodyObject = master.GetBodyObject();
			CharacterBody val = (((Object)(object)bodyObject != (Object)null) ? bodyObject.GetComponent<CharacterBody>() : null);
			if ((Object)(object)val != (Object)null)
			{
				val.wasLucky = true;
			}
		}
	}

	private static void VineOrbPrefix(VineOrb __instance)
	{
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_01be: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_0106: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_013f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0175: Unknown result type (might be due to invalid IL or missing references)
		//IL_017a: Unknown result type (might be due to invalid IL or missing references)
		if (!MergeThornStacks.Value || __instance.splitDebuffInformation == null)
		{
			return;
		}
		int num = Math.Max(1, ThornStackCap.Value);
		HurtBox target = ((Orb)__instance).target;
		CharacterBody val = (((Object)(object)target != (Object)null && (Object)(object)target.healthComponent != (Object)null) ? target.healthComponent.body : null);
		bool flag = false;
		foreach (SplitDebuffInformation item in __instance.splitDebuffInformation)
		{
			flag |= item.count > num;
		}
		if (!flag)
		{
			return;
		}
		List<SplitDebuffInformation> list = (__instance.splitDebuffInformation = new List<SplitDebuffInformation>(__instance.splitDebuffInformation));
		for (int i = 0; i < list.Count; i++)
		{
			SplitDebuffInformation value = list[i];
			if (value.count <= num)
			{
				continue;
			}
			BuffDef buffDef = BuffCatalog.GetBuffDef(value.index);
			if ((Object)(object)buffDef != (Object)null && buffDef.isDOT && (Object)(object)val != (Object)null)
			{
				DotIndex dotDefIndex = DotController.GetDotDefIndex(buffDef);
				DotDef dotDef = DotController.GetDotDef(dotDefIndex);
				if (dotDef == null)
				{
					continue;
				}
				InflictDotInfo val2 = new InflictDotInfo
				{
					attackerObject = value.attacker,
					victimObject = ((Component)val).gameObject,
					dotIndex = dotDefIndex,
					duration = ((value.duration == 0f) ? dotDef.interval : value.duration),
					hitHurtBox = target,
					procChainMask = value.procChainMask,
					damageMultiplier = (float)value.count / (float)num
				};
				for (int j = 0; j < num; j++)
				{
					DotController.InflictDot(ref val2);
				}
				value.count = 0;
			}
			else
			{
				value.count = num;
			}
			list[i] = value;
		}
	}

	private static bool IsMerged(string prefabName)
	{
		if (mergedListSource != MergedProjectiles.Value)
		{
			mergedListSource = MergedProjectiles.Value;
			mergedList = SplitNames(mergedListSource);
		}
		string[] array = mergedList;
		foreach (string value in array)
		{
			if (prefabName.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
		}
		return false;
	}

	internal static string[] SplitNames(string text)
	{
		List<string> list = new List<string>();
		string[] array = (text ?? "").Split(',');
		foreach (string text2 in array)
		{
			string text3 = text2.Trim();
			if (text3.Length > 0)
			{
				list.Add(text3);
			}
		}
		return list.ToArray();
	}

	private static bool FireProjectilePrefix(ref FireProjectileInfo fireProjectileInfo)
	{
		GameObject projectilePrefab = fireProjectileInfo.projectilePrefab;
		if (LogEffectNames.Value && (Object)(object)projectilePrefab != (Object)null)
		{
			Log("Снаряд: " + ((Object)projectilePrefab).name);
		}
		if (!MergeProjectiles.Value || !NetworkServer.active || (Object)(object)projectilePrefab == (Object)null || (Object)(object)fireProjectileInfo.owner == (Object)null)
		{
			return true;
		}
		if (!IsMerged(((Object)projectilePrefab).name))
		{
			return true;
		}
		string key = ((Object)fireProjectileInfo.owner).GetInstanceID() + "/" + ((Object)projectilePrefab).name;
		if (!budgets.TryGetValue(key, out var value))
		{
			value = new ProjectileBudget();
			budgets[key] = value;
		}
		float time = Time.time;
		if (time - value.windowStart >= 1f)
		{
			value.windowStart = time;
			value.fired = 0;
		}
		if (value.fired >= Math.Max(1, ProjectilesPerSecond.Value))
		{
			value.bankedDamage += fireProjectileInfo.damage;
			return false;
		}
		value.fired++;
		fireProjectileInfo.damage += value.bankedDamage;
		value.bankedDamage = 0f;
		return true;
	}

	private static bool AllowEffect(int key, string name)
	{
		if (LogEffectNames.Value && name != null)
		{
			Log("Эффект: " + name);
		}
		if (!ThrottleEffects.Value)
		{
			return true;
		}
		if (!effectBudgets.TryGetValue(key, out var value))
		{
			value = new EffectBudget();
			effectBudgets[key] = value;
		}
		float unscaledTime = Time.unscaledTime;
		if (unscaledTime - value.windowStart >= 1f)
		{
			value.windowStart = unscaledTime;
			value.shown = 0;
		}
		if (value.shown >= Math.Max(1, EffectsPerSecond.Value))
		{
			return false;
		}
		value.shown++;
		return true;
	}

	private static bool SpawnEffectPrefabPrefix(GameObject effectPrefab)
	{
		if ((Object)(object)effectPrefab == (Object)null)
		{
			return true;
		}
		return AllowEffect(((Object)effectPrefab).GetInstanceID(), ((Object)effectPrefab).name);
	}

	private static bool SpawnEffectIndexPrefix(EffectIndex effectIndex)
	{
		//IL_0000: 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_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Expected I4, but got Unknown
		EffectDef effectDef = EffectCatalog.GetEffectDef(effectIndex);
		return AllowEffect(int.MinValue + effectIndex, (effectDef != null && (Object)(object)effectDef.prefab != (Object)null) ? ((Object)effectDef.prefab).name : null);
	}

	private static bool AwardExperiencePrefix(CharacterBody body, ulong amount)
	{
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		if (!InstantExperience.Value || !NetworkServer.active || (Object)(object)TeamManager.instance == (Object)null)
		{
			return true;
		}
		if (SuppressExperience.suppressExperience || (Object)(object)body == (Object)null || (Object)(object)body.master == (Object)null)
		{
			return false;
		}
		TeamManager.instance.GiveTeamExperience(body.master.teamIndex, amount);
		return false;
	}

	private static bool AddOrbPrefix(Orb orb)
	{
		if (!InstantGold.Value || !NetworkServer.active)
		{
			return true;
		}
		GoldOrb val = (GoldOrb)(object)((orb is GoldOrb) ? orb : null);
		if (val == null)
		{
			return true;
		}
		((Orb)val).OnArrival();
		return false;
	}
}
internal static class BlackHole
{
	public const string DefaultNames = "ElementalRingVoidBlackHole,NullifierDeathBomb,VoidJailerDeathBomb,VoidMegaCrabDeathBomb,VoidDeathBomb,GravSphere";

	private static string namesSource;

	private static string[] names = new string[0];

	private static readonly string[] BlindingMaterialWords = new string[3] { "distort", "flash", "screen" };

	public static void Patch(Harmony harmony)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Expected O, but got Unknown
		HarmonyMethod val = new HarmonyMethod(typeof(BlackHole), "SpawnedPostfix", (Type[])null);
		harmony.Patch((MethodBase)AccessTools.Method(typeof(EffectComponent), "Start", (Type[])null, (Type[])null), (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)AccessTools.Method(typeof(ProjectileGhostController), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)AccessTools.Method(typeof(ProjectileController), "Start", (Type[])null, (Type[])null), (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
	}

	private static void SpawnedPostfix(MonoBehaviour __instance)
	{
		if (LagFixPlugin.TameBlackHoles.Value && !((Object)(object)__instance == (Object)null))
		{
			GameObject gameObject = ((Component)__instance).gameObject;
			if (Matches(((Object)gameObject).name))
			{
				Tame(gameObject);
			}
		}
	}

	private static bool Matches(string objectName)
	{
		if (namesSource != LagFixPlugin.BlackHoleNames.Value)
		{
			namesSource = LagFixPlugin.BlackHoleNames.Value;
			names = LagFixPlugin.SplitNames(namesSource);
		}
		string[] array = names;
		foreach (string value in array)
		{
			if (objectName.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
		}
		return false;
	}

	private static void Tame(GameObject go)
	{
		Light[] componentsInChildren = go.GetComponentsInChildren<Light>(true);
		foreach (Light val in componentsInChildren)
		{
			((Behaviour)val).enabled = false;
		}
		Behaviour[] componentsInChildren2 = go.GetComponentsInChildren<Behaviour>(true);
		foreach (Behaviour val2 in componentsInChildren2)
		{
			string name = ((object)val2).GetType().Name;
			if (name == "PostProcessVolume" || name == "PostProcessDuration")
			{
				val2.enabled = false;
			}
		}
		Renderer[] componentsInChildren3 = go.GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val3 in componentsInChildren3)
		{
			Material sharedMaterial = val3.sharedMaterial;
			if ((Object)(object)sharedMaterial == (Object)null)
			{
				continue;
			}
			string text = (((Object)sharedMaterial).name + " " + (((Object)(object)sharedMaterial.shader != (Object)null) ? ((Object)sharedMaterial.shader).name : "")).ToLowerInvariant();
			string[] blindingMaterialWords = BlindingMaterialWords;
			foreach (string value in blindingMaterialWords)
			{
				if (text.Contains(value))
				{
					val3.enabled = false;
					break;
				}
			}
		}
	}
}