Decompiled source of VelocityDamage v1.0.0

VelocityDamage.dll

Decompiled 3 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
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: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("McHorse")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+8d973f08bd55896d534c97eba8dec604eb08c189")]
[assembly: AssemblyProduct("VelocityDamage")]
[assembly: AssemblyTitle("VelocityDamage")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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 VelocityDamage
{
	internal sealed class FormulaEngine
	{
		private interface IFormulaNode
		{
			bool TryEvaluate(out float value, out string? error);
		}

		private sealed class ConstantNode : IFormulaNode
		{
			private readonly float _value;

			internal ConstantNode(float value)
			{
				_value = value;
			}

			public bool TryEvaluate(out float value, out string? error)
			{
				value = _value;
				error = null;
				return true;
			}
		}

		private sealed class VariableBinding
		{
			internal float Value { get; set; }

			internal VariableBinding(float value)
			{
				Value = value;
			}
		}

		private sealed class VariableNode : IFormulaNode
		{
			private readonly string _name;

			private readonly VariableBinding _binding;

			internal VariableNode(string name, VariableBinding binding)
			{
				_name = name;
				_binding = binding;
			}

			public bool TryEvaluate(out float value, out string? error)
			{
				value = _binding.Value;
				error = null;
				return true;
			}
		}

		private sealed class UnaryNegateNode : IFormulaNode
		{
			private readonly IFormulaNode _inner;

			internal UnaryNegateNode(IFormulaNode inner)
			{
				_inner = inner;
			}

			public bool TryEvaluate(out float value, out string? error)
			{
				if (!_inner.TryEvaluate(out var value2, out error))
				{
					value = 0f;
					return false;
				}
				value = 0f - value2;
				return true;
			}
		}

		private sealed class BinaryOperatorNode : IFormulaNode
		{
			private readonly string _operator;

			private readonly IFormulaNode _left;

			private readonly IFormulaNode _right;

			internal BinaryOperatorNode(string @operator, IFormulaNode left, IFormulaNode right)
			{
				_operator = @operator;
				_left = left;
				_right = right;
			}

			public bool TryEvaluate(out float value, out string? error)
			{
				value = 0f;
				if (!_left.TryEvaluate(out var value2, out error))
				{
					return false;
				}
				if (!_right.TryEvaluate(out var value3, out error))
				{
					return false;
				}
				string @operator = _operator;
				if (1 == 0)
				{
				}
				float num = @operator switch
				{
					"+" => value2 + value3, 
					"-" => value2 - value3, 
					"*" => value2 * value3, 
					"/" => (Math.Abs(value3) < 1E-05f) ? 0f : (value2 / value3), 
					"%" => (Math.Abs(value3) < 1E-05f) ? 0f : (value2 % value3), 
					"^" => (float)Math.Pow(value2, value3), 
					_ => throw new InvalidOperationException("Unsupported operator '" + _operator + "'."), 
				};
				if (1 == 0)
				{
				}
				value = num;
				error = null;
				return true;
			}
		}

		private sealed class Parser
		{
			private readonly List<Token> _tokens;

			private int _index;

			internal Token Current => _tokens[_index];

			internal bool IsAtEnd => Current.Type == TokenType.End;

			internal Parser(List<Token> tokens)
			{
				_tokens = tokens;
				_index = 0;
			}

			internal bool TryParseExpression(Dictionary<string, VariableBinding> variables, out IFormulaNode? node, out string? error)
			{
				return TryParseAddSub(variables, out node, out error);
			}

			private bool TryParseAddSub(Dictionary<string, VariableBinding> variables, out IFormulaNode? node, out string? error)
			{
				if (!TryParseMulDiv(variables, out node, out error) || node == null)
				{
					return false;
				}
				while (Match("+") || Match("-"))
				{
					string text = Previous().Text;
					if (!TryParseMulDiv(variables, out IFormulaNode node2, out error) || node2 == null)
					{
						return false;
					}
					node = new BinaryOperatorNode(text, node, node2);
				}
				return true;
			}

			private bool TryParseMulDiv(Dictionary<string, VariableBinding> variables, out IFormulaNode? node, out string? error)
			{
				if (!TryParsePowMod(variables, out node, out error) || node == null)
				{
					return false;
				}
				while (Match("*") || Match("/"))
				{
					string text = Previous().Text;
					if (!TryParsePowMod(variables, out IFormulaNode node2, out error) || node2 == null)
					{
						return false;
					}
					node = new BinaryOperatorNode(text, node, node2);
				}
				return true;
			}

			private bool TryParsePowMod(Dictionary<string, VariableBinding> variables, out IFormulaNode? node, out string? error)
			{
				if (!TryParseUnary(variables, out node, out error) || node == null)
				{
					return false;
				}
				while (Match("^") || Match("%"))
				{
					string text = Previous().Text;
					if (!TryParseUnary(variables, out IFormulaNode node2, out error) || node2 == null)
					{
						return false;
					}
					node = new BinaryOperatorNode(text, node, node2);
				}
				return true;
			}

			private bool TryParseUnary(Dictionary<string, VariableBinding> variables, out IFormulaNode? node, out string? error)
			{
				error = null;
				if (Match("-"))
				{
					if (!TryParseUnary(variables, out IFormulaNode node2, out error) || node2 == null)
					{
						node = null;
						return false;
					}
					node = new UnaryNegateNode(node2);
					return true;
				}
				return TryParsePrimary(variables, out node, out error);
			}

			private bool TryParsePrimary(Dictionary<string, VariableBinding> variables, out IFormulaNode? node, out string? error)
			{
				error = null;
				if (MatchType(TokenType.Number))
				{
					node = new ConstantNode(Previous().NumberValue);
					return true;
				}
				if (MatchType(TokenType.Variable))
				{
					string text = Previous().Text;
					if (!variables.TryGetValue(text, out VariableBinding value))
					{
						error = "Unknown variable '" + text + "'. Register variables before parsing.";
						node = null;
						return false;
					}
					node = new VariableNode(text, value);
					return true;
				}
				if (Match("("))
				{
					if (!TryParseExpression(variables, out node, out error) || node == null)
					{
						return false;
					}
					if (!Match(")"))
					{
						error = "Missing closing parenthesis.";
						return false;
					}
					return true;
				}
				error = "Unexpected token '" + Current.Text + "'.";
				node = null;
				return false;
			}

			private bool Match(string symbol)
			{
				if (Current.Type == TokenType.Symbol && Current.Text == symbol)
				{
					_index++;
					return true;
				}
				return false;
			}

			private bool MatchType(TokenType type)
			{
				if (Current.Type == type)
				{
					_index++;
					return true;
				}
				return false;
			}

			private Token Previous()
			{
				return _tokens[_index - 1];
			}
		}

		private enum TokenType
		{
			Number,
			Variable,
			Symbol,
			End
		}

		private readonly struct Token
		{
			internal readonly TokenType Type;

			internal readonly string Text;

			internal readonly float NumberValue;

			internal Token(TokenType type, string text, float numberValue)
			{
				Type = type;
				Text = text;
				NumberValue = numberValue;
			}
		}

		private readonly Dictionary<string, VariableBinding> _variables = new Dictionary<string, VariableBinding>(StringComparer.OrdinalIgnoreCase);

		private IFormulaNode _root = new ConstantNode(0f);

		internal FormulaEngine()
		{
		}

		internal static bool TryCreate(string expression, out FormulaEngine? engine, out string? error)
		{
			engine = new FormulaEngine();
			return engine.TryParse(expression, out error);
		}

		internal static FormulaEngine FromKnownValid(string expression)
		{
			if (!TryCreate(expression, out FormulaEngine engine, out string _))
			{
				throw new InvalidOperationException("Expected valid formula.");
			}
			return engine;
		}

		internal void RegisterVariable(string name, float initialValue = 0f)
		{
			_variables[name] = new VariableBinding(initialValue);
		}

		internal void SetVariableValue(string name, float value)
		{
			if (_variables.TryGetValue(name, out VariableBinding value2))
			{
				value2.Value = value;
			}
		}

		internal bool TryParse(string expression, out string? error)
		{
			error = null;
			if (string.IsNullOrWhiteSpace(expression))
			{
				error = "Expression is empty.";
				return false;
			}
			if (!Tokenize(expression, out List<Token> tokens, out error) || tokens == null)
			{
				return false;
			}
			Parser parser = new Parser(tokens);
			if (!parser.TryParseExpression(_variables, out IFormulaNode node, out error) || node == null)
			{
				return false;
			}
			if (!parser.IsAtEnd)
			{
				error = "Unexpected token '" + parser.Current.Text + "'.";
				return false;
			}
			_root = node;
			return true;
		}

		internal bool TryEvaluate(out float value, out string? error)
		{
			return _root.TryEvaluate(out value, out error);
		}

		private static bool Tokenize(string expression, out List<Token>? tokens, out string? error)
		{
			tokens = new List<Token>();
			error = null;
			int i = 0;
			while (i < expression.Length)
			{
				char c = expression[i];
				if (char.IsWhiteSpace(c))
				{
					i++;
					continue;
				}
				if (char.IsDigit(c) || c == '.')
				{
					int num = i;
					for (i++; i < expression.Length && (char.IsDigit(expression[i]) || expression[i] == '.'); i++)
					{
					}
					int num2 = num;
					string text = expression.Substring(num2, i - num2);
					if (!float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
					{
						error = "Invalid number '" + text + "'.";
						return false;
					}
					tokens.Add(new Token(TokenType.Number, text, result));
					continue;
				}
				if (char.IsLetter(c) || c == '_')
				{
					int num3 = i;
					for (i++; i < expression.Length && (char.IsLetterOrDigit(expression[i]) || expression[i] == '_'); i++)
					{
					}
					int num2 = num3;
					string text2 = expression.Substring(num2, i - num2);
					tokens.Add(new Token(TokenType.Variable, text2, 0f));
					continue;
				}
				bool flag;
				switch (c)
				{
				case '%':
				case '(':
				case ')':
				case '*':
				case '+':
				case '-':
				case '/':
				case '^':
					flag = true;
					break;
				default:
					flag = false;
					break;
				}
				if (flag)
				{
					tokens.Add(new Token(TokenType.Symbol, c.ToString(), 0f));
					i++;
					continue;
				}
				error = $"Unexpected character '{c}'.";
				return false;
			}
			tokens.Add(new Token(TokenType.End, string.Empty, 0f));
			return true;
		}
	}
	internal static class FormulaEngineSelfTests
	{
		private const float Epsilon = 0.0001f;

		internal static void Run(ManualLogSource logger)
		{
			int passed = 0;
			int failed = 0;
			Execute(logger, "PEDMAS", "2 + 3 * 4 - 8 / 2", 10f, null, ref passed, ref failed);
			Execute(logger, "Variables", "d + v * l", 16f, delegate(FormulaEngine engine)
			{
				engine.SetVariableValue("d", 4f);
				engine.SetVariableValue("v", 3f);
				engine.SetVariableValue("l", 4f);
			}, ref passed, ref failed);
			Execute(logger, "Negation (parenthesis)", "-(2 + 3)", -5f, null, ref passed, ref failed);
			Execute(logger, "Negation (variable)", "-d", -7f, delegate(FormulaEngine engine)
			{
				engine.SetVariableValue("d", 7f);
			}, ref passed, ref failed);
			Execute(logger, "Negation (constant)", "-2 + 5", 3f, null, ref passed, ref failed);
			Execute(logger, "Parenthesis", "(2 + 3) * (4 - 1)", 15f, null, ref passed, ref failed);
			logger.LogInfo((object)$"FormulaEngine tests complete. Passed: {passed}, Failed: {failed}");
		}

		private static void Execute(ManualLogSource logger, string name, string expression, float expected, Action<FormulaEngine>? setupVariables, ref int passed, ref int failed)
		{
			FormulaEngine formulaEngine = CreateTestEngine();
			if (!formulaEngine.TryParse(expression, out string error))
			{
				failed++;
				logger.LogError((object)("[FormulaEngine Test] " + name + " FAILED: parse error: " + error));
				return;
			}
			setupVariables?.Invoke(formulaEngine);
			if (!formulaEngine.TryEvaluate(out float value, out string error2))
			{
				failed++;
				logger.LogError((object)("[FormulaEngine Test] " + name + " FAILED: eval error: " + error2));
			}
			else if (Math.Abs(value - expected) <= 0.0001f)
			{
				passed++;
				logger.LogInfo((object)$"[FormulaEngine Test] {name} PASSED: expected={expected}, actual={value}");
			}
			else
			{
				failed++;
				logger.LogError((object)$"[FormulaEngine Test] {name} FAILED: expected={expected}, actual={value}");
			}
		}

		private static FormulaEngine CreateTestEngine()
		{
			FormulaEngine formulaEngine = new FormulaEngine();
			formulaEngine.RegisterVariable("d");
			formulaEngine.RegisterVariable("damage");
			formulaEngine.RegisterVariable("v");
			formulaEngine.RegisterVariable("velocity");
			formulaEngine.RegisterVariable("l");
			formulaEngine.RegisterVariable("level");
			return formulaEngine;
		}
	}
	[HarmonyPatch]
	internal static class PhysGrabObjectImpactDetector_OnCollisionStay_Patch
	{
		[ThreadStatic]
		private static bool _isInCollisionHurtScope;

		[ThreadStatic]
		private static float _currentRelativeSpeed;

		internal static bool IsInCollisionHurtScope => _isInCollisionHurtScope;

		internal static float CurrentRelativeSpeed => _currentRelativeSpeed;

		private static MethodBase? TargetMethod()
		{
			Type type = AccessTools.TypeByName("PhysGrabObjectImpactDetector");
			return ((object)type == null) ? null : AccessTools.Method(type, "OnCollisionStay", new Type[1] { typeof(Collision) }, (Type[])null);
		}

		private static void Prefix(Collision collision)
		{
			//IL_0008: 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)
			_isInCollisionHurtScope = true;
			Vector3 relativeVelocity = collision.relativeVelocity;
			_currentRelativeSpeed = ((Vector3)(ref relativeVelocity)).magnitude;
		}

		private static void Finalizer()
		{
			_isInCollisionHurtScope = false;
			_currentRelativeSpeed = 0f;
		}
	}
	[HarmonyPatch]
	internal static class EnemyHealth_Hurt_Patch
	{
		private static MethodBase? TargetMethod()
		{
			Type type = AccessTools.TypeByName("EnemyHealth");
			return ((object)type == null) ? null : AccessTools.Method(type, "Hurt", new Type[2]
			{
				typeof(int),
				typeof(Vector3)
			}, (Type[])null);
		}

		private static void Prefix(ref int __0)
		{
			if (PhysGrabObjectImpactDetector_OnCollisionStay_Patch.IsInCollisionHurtScope)
			{
				float currentRelativeSpeed = PhysGrabObjectImpactDetector_OnCollisionStay_Patch.CurrentRelativeSpeed;
				__0 = VelocityDamage.Instance.ScaleCollisionDamage(__0, currentRelativeSpeed);
			}
			else if (HurtCollider_EnemyHurt_TumbleScope_Patch.IsInTumbleEnemyHurtScope)
			{
				float currentTumbleVelocity = HurtCollider_EnemyHurt_TumbleScope_Patch.CurrentTumbleVelocity;
				__0 = VelocityDamage.Instance.ScaleTumblingPlayerEnemyDamage(__0, currentTumbleVelocity);
			}
		}
	}
	[HarmonyPatch]
	internal static class HurtCollider_EnemyHurt_TumbleScope_Patch
	{
		[ThreadStatic]
		private static bool _isInTumbleEnemyHurtScope;

		[ThreadStatic]
		private static float _currentTumbleVelocity;

		private static readonly Type? PlayerTumbleType = AccessTools.TypeByName("PlayerTumble");

		private static readonly FieldInfo? PlayerTumblePhysGrabObjectField = (((object)PlayerTumbleType == null) ? null : AccessTools.Field(PlayerTumbleType, "physGrabObject"));

		private static readonly PropertyInfo? PhysGrabObjectRbVelocityProperty = AccessTools.Property(AccessTools.TypeByName("PhysGrabObject"), "rbVelocity");

		private static readonly FieldInfo? PhysGrabObjectRbVelocityField = AccessTools.Field(AccessTools.TypeByName("PhysGrabObject"), "rbVelocity");

		internal static bool IsInTumbleEnemyHurtScope => _isInTumbleEnemyHurtScope;

		internal static float CurrentTumbleVelocity => _currentTumbleVelocity;

		private static MethodBase? TargetMethod()
		{
			Type type = AccessTools.TypeByName("HurtCollider");
			return ((object)type == null) ? null : AccessTools.Method(type, "EnemyHurt", (Type[])null, (Type[])null);
		}

		private static void Prefix(object __instance)
		{
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			_isInTumbleEnemyHurtScope = false;
			_currentTumbleVelocity = 0f;
			Component val = (Component)((__instance is Component) ? __instance : null);
			if (val == null || (object)PlayerTumbleType == null)
			{
				return;
			}
			Component componentInParent = val.GetComponentInParent(PlayerTumbleType);
			if (componentInParent != null)
			{
				float currentTumbleVelocity = 0f;
				object obj = PlayerTumblePhysGrabObjectField?.GetValue(componentInParent);
				if (obj != null && (PhysGrabObjectRbVelocityProperty?.GetValue(obj) ?? PhysGrabObjectRbVelocityField?.GetValue(obj)) is Vector3 val2)
				{
					currentTumbleVelocity = ((Vector3)(ref val2)).magnitude;
				}
				_isInTumbleEnemyHurtScope = true;
				_currentTumbleVelocity = currentTumbleVelocity;
			}
		}

		private static void Finalizer()
		{
			_isInTumbleEnemyHurtScope = false;
			_currentTumbleVelocity = 0f;
		}
	}
	[BepInPlugin("McHorse.VelocityDamage", "VelocityDamage", "1.0")]
	public class VelocityDamage : BaseUnityPlugin
	{
		private const string DefaultFormula = "d * (1 + (v / 4) ^ 0.5)";

		private const string DefaultTumbleEnemyFormula = "d * (1 + (v / 4) ^ 0.5)";

		private ConfigEntry<string>? _damageFormulaConfig;

		private ConfigEntry<string>? _tumbleEnemyDamageFormulaConfig;

		private ConfigEntry<bool>? _debugDamageTestsConfig;

		private FormulaEngine? _formulaEngine;

		private FormulaEngine? _tumbleEnemyFormulaEngine;

		internal static VelocityDamage Instance { get; private set; }

		internal static ManualLogSource Logger => Instance._logger;

		private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger;

		internal Harmony? Harmony { get; set; }

		private void Awake()
		{
			Instance = this;
			((Component)this).gameObject.transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			_damageFormulaConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Damage", "Collision damage", "d * (1 + (v / 4) ^ 0.5)", "Formula variables: d/damage, v/velocity, l/level (current level number, 1-based). Supported operators: +, -, *, /, % (modulo), ^ (power), and parentheses.");
			_damageFormulaConfig.SettingChanged += OnDamageFormulaChanged;
			_tumbleEnemyDamageFormulaConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Damage", "Tumble damage", "d * (1 + (v / 4) ^ 0.5)", "Formula used when a tumbling player damages an enemy. Variables: d/damage, v/velocity, l/level (current level number, 1-based). Supported operators: +, -, *, /, % (modulo), ^ (power), and parentheses.");
			_tumbleEnemyDamageFormulaConfig.SettingChanged += OnTumbleEnemyDamageFormulaChanged;
			_debugDamageTestsConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Damage", "Debug", false, "Enable FormulaEngine startup self-tests logging.");
			_formulaEngine = CreateFormulaEngine();
			_tumbleEnemyFormulaEngine = CreateFormulaEngine();
			ReloadFormulaEngine(_damageFormulaConfig.Value, isRuntimeChange: false);
			ReloadTumbleEnemyFormulaEngine(_tumbleEnemyDamageFormulaConfig.Value, isRuntimeChange: false);
			ConfigEntry<bool>? debugDamageTestsConfig = _debugDamageTestsConfig;
			if (debugDamageTestsConfig != null && debugDamageTestsConfig.Value)
			{
				FormulaEngineSelfTests.Run(Logger);
			}
			Patch();
			Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!");
		}

		internal void Patch()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0026: Expected O, but got Unknown
			if (Harmony == null)
			{
				Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
				Harmony val2 = val;
				Harmony = val;
			}
			Harmony.PatchAll();
		}

		internal void Unpatch()
		{
			Harmony? harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void OnDestroy()
		{
			if (_damageFormulaConfig != null)
			{
				_damageFormulaConfig.SettingChanged -= OnDamageFormulaChanged;
			}
			if (_tumbleEnemyDamageFormulaConfig != null)
			{
				_tumbleEnemyDamageFormulaConfig.SettingChanged -= OnTumbleEnemyDamageFormulaChanged;
			}
		}

		private void OnDamageFormulaChanged(object sender, EventArgs e)
		{
			if (_damageFormulaConfig != null)
			{
				ReloadFormulaEngine(_damageFormulaConfig.Value, isRuntimeChange: true);
			}
		}

		private void OnTumbleEnemyDamageFormulaChanged(object sender, EventArgs e)
		{
			if (_tumbleEnemyDamageFormulaConfig != null)
			{
				ReloadTumbleEnemyFormulaEngine(_tumbleEnemyDamageFormulaConfig.Value, isRuntimeChange: true);
			}
		}

		private FormulaEngine CreateFormulaEngine()
		{
			FormulaEngine formulaEngine = new FormulaEngine();
			formulaEngine.RegisterVariable("d");
			formulaEngine.RegisterVariable("damage");
			formulaEngine.RegisterVariable("v");
			formulaEngine.RegisterVariable("velocity");
			formulaEngine.RegisterVariable("l");
			formulaEngine.RegisterVariable("level");
			return formulaEngine;
		}

		private void ReloadFormulaEngine(string formulaText, bool isRuntimeChange)
		{
			string error = null;
			if (_formulaEngine != null && _formulaEngine.TryParse(formulaText, out error))
			{
				Logger.LogInfo((object)("Damage formula " + (isRuntimeChange ? "updated" : "loaded") + ": " + formulaText));
				return;
			}
			if (isRuntimeChange)
			{
				Logger.LogWarning((object)("Rejected invalid runtime formula '" + formulaText + "'. Keeping previous formula. Error: " + error));
				return;
			}
			Logger.LogWarning((object)("Invalid startup formula '" + formulaText + "'. Using default 'd * (1 + (v / 4) ^ 0.5)'. Error: " + error));
			_formulaEngine?.TryParse("d * (1 + (v / 4) ^ 0.5)", out string _);
		}

		private void ReloadTumbleEnemyFormulaEngine(string formulaText, bool isRuntimeChange)
		{
			string error = null;
			if (_tumbleEnemyFormulaEngine != null && _tumbleEnemyFormulaEngine.TryParse(formulaText, out error))
			{
				Logger.LogInfo((object)("Tumble-enemy formula " + (isRuntimeChange ? "updated" : "loaded") + ": " + formulaText));
				return;
			}
			if (isRuntimeChange)
			{
				Logger.LogWarning((object)("Rejected invalid runtime tumble-enemy formula '" + formulaText + "'. Keeping previous formula. Error: " + error));
				return;
			}
			Logger.LogWarning((object)("Invalid startup tumble-enemy formula '" + formulaText + "'. Using default 'd * (1 + (v / 4) ^ 0.5)'. Error: " + error));
			_tumbleEnemyFormulaEngine?.TryParse("d * (1 + (v / 4) ^ 0.5)", out string _);
		}

		internal int ScaleCollisionDamage(int baseDamage, float velocityDistance)
		{
			float value = SemiFunc.RunGetLevelsCompleted() + 1;
			if (_formulaEngine == null)
			{
				return Mathf.RoundToInt((float)baseDamage * (1f + Mathf.Sqrt(velocityDistance / 4f)));
			}
			_formulaEngine.SetVariableValue("d", baseDamage);
			_formulaEngine.SetVariableValue("damage", baseDamage);
			_formulaEngine.SetVariableValue("v", velocityDistance);
			_formulaEngine.SetVariableValue("velocity", velocityDistance);
			_formulaEngine.SetVariableValue("l", value);
			_formulaEngine.SetVariableValue("level", value);
			if (!_formulaEngine.TryEvaluate(out float value2, out string error))
			{
				Logger.LogWarning((object)("Failed to evaluate formula. Falling back to default. Error: " + error));
				return Mathf.RoundToInt((float)baseDamage * (1f + Mathf.Sqrt(velocityDistance / 4f)));
			}
			return Mathf.RoundToInt(value2);
		}

		internal int ScaleTumblingPlayerEnemyDamage(int baseDamage, float velocityDistance)
		{
			float value = SemiFunc.RunGetLevelsCompleted() + 1;
			if (_tumbleEnemyFormulaEngine == null)
			{
				return Mathf.RoundToInt((float)baseDamage * (1f + Mathf.Sqrt(velocityDistance / 4f)));
			}
			_tumbleEnemyFormulaEngine.SetVariableValue("d", baseDamage);
			_tumbleEnemyFormulaEngine.SetVariableValue("damage", baseDamage);
			_tumbleEnemyFormulaEngine.SetVariableValue("v", velocityDistance);
			_tumbleEnemyFormulaEngine.SetVariableValue("velocity", velocityDistance);
			_tumbleEnemyFormulaEngine.SetVariableValue("l", value);
			_tumbleEnemyFormulaEngine.SetVariableValue("level", value);
			if (!_tumbleEnemyFormulaEngine.TryEvaluate(out float value2, out string error))
			{
				Logger.LogWarning((object)("Failed to evaluate tumble-enemy formula. Falling back to default. Error: " + error));
				return Mathf.RoundToInt((float)baseDamage * (1f + Mathf.Sqrt(velocityDistance / 4f)));
			}
			return Mathf.RoundToInt(value2);
		}
	}
}