Decompiled source of ShieldBalance v1.0.0

ShieldBalance.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Utils;
using UnityEngine;

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

[BepInPlugin("bub.shieldbalance", "Shield Balance", "1.0.0")]
[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
[SynchronizationMode(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
	private enum Kind
	{
		Buckler,
		Round,
		Tower
	}

	private class Ctx
	{
		public Ctx Prev;

		public bool Applies;

		public Kind Kind;

		public float BlockTimer;

		public bool Staggered;

		public bool StaminaScaled;
	}

	public const string Guid = "bub.shieldbalance";

	public const string Version = "1.0.0";

	private static ManualLogSource Log;

	private static ConfigEntry<bool> GuardBreakStillBlocks;

	private static ConfigEntry<float> BucklerMinParryBonus;

	private static ConfigEntry<float> StaggerBuckler;

	private static ConfigEntry<float> StaggerRound;

	private static ConfigEntry<float> StaggerTower;

	private static ConfigEntry<float> StaminaBuckler;

	private static ConfigEntry<float> StaminaRound;

	private static ConfigEntry<float> StaminaTower;

	private static Func<Character, float, Vector3, HitData, bool> AddStaggerDamage;

	private static FieldInfo BlockTimer;

	private static Func<Humanoid, ItemData> GetCurrentBlocker;

	private static Ctx Current;

	private static HitData lastBlockedHit;

	private static Kind lastBlockedKind;

	private void Awake()
	{
		//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0201: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		GuardBreakStillBlocks = Bind("1 - Guard break", "Guard break still blocks", value: true, "When a blocked hit fills your stagger bar, the shield still reduces it and only the excess reaches your health (vanilla: the whole hit goes through).");
		BucklerMinParryBonus = Bind("2 - Shield types", "Buckler min parry bonus", 2f, "Shields with a parry bonus at or above this are bucklers; with no parry bonus (1.0 or less) they are tower shields; the rest are round shields.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1.01f, 10f));
		StaggerBuckler = Bind("3 - Stagger taken while blocking", "Buckler", 1f, "Multiplier on the stagger you take when blocking with a buckler.", (AcceptableValueBase)(object)Range(0f, 3f));
		StaggerRound = Bind("3 - Stagger taken while blocking", "Round shield", 0.85f, "Multiplier on the stagger you take when blocking with a round shield.", (AcceptableValueBase)(object)Range(0f, 3f));
		StaggerTower = Bind("3 - Stagger taken while blocking", "Tower shield", 0.7f, "Multiplier on the stagger you take when blocking with a tower shield.", (AcceptableValueBase)(object)Range(0f, 3f));
		StaminaBuckler = Bind("4 - Block stamina cost", "Buckler", 1f, "Multiplier on the stamina cost of a normal block (not parry) with a buckler.", (AcceptableValueBase)(object)Range(0f, 3f));
		StaminaRound = Bind("4 - Block stamina cost", "Round shield", 0.85f, "Multiplier on the stamina cost of a normal block (not parry) with a round shield.", (AcceptableValueBase)(object)Range(0f, 3f));
		StaminaTower = Bind("4 - Block stamina cost", "Tower shield", 0.7f, "Multiplier on the stamina cost of a normal block with a tower shield.", (AcceptableValueBase)(object)Range(0f, 3f));
		MethodInfo methodInfo = AccessTools.Method(typeof(Character), "AddStaggerDamage", new Type[3]
		{
			typeof(float),
			typeof(Vector3),
			typeof(HitData)
		}, (Type[])null);
		AddStaggerDamage = AccessTools.MethodDelegate<Func<Character, float, Vector3, HitData, bool>>(methodInfo, (object)null, true);
		BlockTimer = AccessTools.Field(typeof(Humanoid), "m_blockTimer");
		GetCurrentBlocker = AccessTools.MethodDelegate<Func<Humanoid, ItemData>>(AccessTools.Method(typeof(Humanoid), "GetCurrentBlocker", (Type[])null, (Type[])null), (object)null, true);
		Harmony val = new Harmony("bub.shieldbalance");
		MethodInfo methodInfo2 = AccessTools.Method(typeof(Humanoid), "BlockAttack", (Type[])null, (Type[])null);
		val.Patch((MethodBase)methodInfo2, Hm("BlockPrefix"), Hm("BlockPostfix"), Hm("BlockTranspiler"), Hm("BlockFinalizer"), (HarmonyMethod)null);
		val.Patch((MethodBase)AccessTools.Method(typeof(SEMan), "ModifyBlockStaminaUsage", (Type[])null, (Type[])null), (HarmonyMethod)null, Hm("StaminaPostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		val.Patch((MethodBase)AccessTools.Method(typeof(Character), "AddStaggerDamage", (Type[])null, (Type[])null), Hm("DamageStaggerPrefix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
	}

	private ConfigEntry<T> Bind<T>(string section, string key, T value, string description, AcceptableValueBase range = null)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Expected O, but got Unknown
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Expected O, but got Unknown
		ConfigurationManagerAttributes val = new ConfigurationManagerAttributes();
		val.IsAdminOnly = true;
		return ((BaseUnityPlugin)this).Config.Bind<T>(section, key, value, new ConfigDescription(description, range, new object[1] { val }));
	}

	private static AcceptableValueRange<float> Range(float min, float max)
	{
		return new AcceptableValueRange<float>(min, max);
	}

	private static HarmonyMethod Hm(string name)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Expected O, but got Unknown
		return new HarmonyMethod(typeof(Plugin).GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic));
	}

	private static Kind Classify(ItemData item)
	{
		float timedBlockBonus = item.m_shared.m_timedBlockBonus;
		if (timedBlockBonus <= 1f)
		{
			return Kind.Tower;
		}
		if (timedBlockBonus >= BucklerMinParryBonus.Value)
		{
			return Kind.Buckler;
		}
		return Kind.Round;
	}

	private static float Pick(Kind kind, ConfigEntry<float> buckler, ConfigEntry<float> round, ConfigEntry<float> tower)
	{
		return kind switch
		{
			Kind.Round => round.Value, 
			Kind.Buckler => buckler.Value, 
			_ => tower.Value, 
		};
	}

	private static void BlockPrefix(Humanoid __instance, HitData hit, out Ctx __state)
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Invalid comparison between Unknown and I4
		Ctx ctx = new Ctx();
		ctx.Prev = Current;
		ItemData val = GetCurrentBlocker(__instance);
		ctx.Applies = __instance is Player && val != null && (int)val.m_shared.m_itemType == 5;
		if (ctx.Applies)
		{
			ctx.Kind = Classify(val);
			ctx.BlockTimer = (float)BlockTimer.GetValue(__instance);
		}
		Current = ctx;
		__state = ctx;
	}

	private static Exception BlockFinalizer(Exception __exception, Ctx __state)
	{
		if (__state != null)
		{
			Current = __state.Prev;
		}
		return __exception;
	}

	private static IEnumerable<CodeInstruction> BlockTranspiler(IEnumerable<CodeInstruction> instructions)
	{
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		MethodInfo original = AccessTools.Method(typeof(Character), "AddStaggerDamage", (Type[])null, (Type[])null);
		int num = list.FindIndex((CodeInstruction ci) => CodeInstructionExtensions.Calls(ci, original));
		if (num < 0 || num + 1 >= list.Count || !CodeInstructionExtensions.IsStloc(list[num + 1], (LocalBuilder)null))
		{
			Log.LogError((object)"Humanoid.BlockAttack mudou (AddStaggerDamage não encontrado); patch não aplicado.");
			return list;
		}
		list[num].opcode = OpCodes.Call;
		list[num].operand = AccessTools.Method(typeof(Plugin), "StaggerHook", (Type[])null, (Type[])null);
		object operand = list[num + 1].operand;
		for (int num2 = num + 2; num2 + 1 < list.Count; num2++)
		{
			if (CodeInstructionExtensions.IsLdloc(list[num2], (LocalBuilder)null) && SameLocal(list[num2], operand) && (list[num2 + 1].opcode == OpCodes.Brtrue || list[num2 + 1].opcode == OpCodes.Brtrue_S))
			{
				list[num2].opcode = OpCodes.Call;
				list[num2].operand = AccessTools.Method(typeof(Plugin), "SkipBlockReduction", (Type[])null, (Type[])null);
				return list;
			}
		}
		Log.LogError((object)"Humanoid.BlockAttack mudou (teste de stagger não encontrado); quebra de guarda fica como no vanilla.");
		return list;
	}

	private static bool SameLocal(CodeInstruction ldloc, object stlocOperand)
	{
		LocalBuilder localBuilder = ldloc.operand as LocalBuilder;
		LocalBuilder localBuilder2 = stlocOperand as LocalBuilder;
		if (localBuilder != null && localBuilder2 != null)
		{
			return localBuilder.LocalIndex == localBuilder2.LocalIndex;
		}
		return object.Equals(ldloc.operand, stlocOperand);
	}

	private static bool StaggerHook(Character self, float damage, Vector3 dir, HitData hit)
	{
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		Ctx current = Current;
		if (current != null && current.Applies)
		{
			damage *= Pick(current.Kind, StaggerBuckler, StaggerRound, StaggerTower);
		}
		bool flag = AddStaggerDamage(self, damage, dir, hit);
		if (current != null)
		{
			current.Staggered = flag;
		}
		return flag;
	}

	private static bool SkipBlockReduction()
	{
		Ctx current = Current;
		if (current == null || !current.Staggered)
		{
			return false;
		}
		if (current.Applies)
		{
			return !GuardBreakStillBlocks.Value;
		}
		return true;
	}

	private static void BlockPostfix(HitData hit, bool __result, Ctx __state)
	{
		if (__state != null && __state.Applies && __result)
		{
			lastBlockedHit = hit;
			lastBlockedKind = __state.Kind;
		}
	}

	private static void DamageStaggerPrefix(ref float __0, HitData __2)
	{
		if (__2 != null && __2 == lastBlockedHit)
		{
			__0 *= Pick(lastBlockedKind, StaggerBuckler, StaggerRound, StaggerTower);
		}
	}

	private static void StaminaPostfix(ref float __1)
	{
		Ctx current = Current;
		if (current != null && current.Applies && !current.StaminaScaled)
		{
			current.StaminaScaled = true;
			if ((current.Kind == Kind.Tower || !(current.BlockTimer >= 0f) || !(current.BlockTimer < 0.25f)) && __1 > 0f)
			{
				__1 *= Pick(current.Kind, StaminaBuckler, StaminaRound, StaminaTower);
			}
		}
	}
}