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 _tokens; private int _index; internal Token Current => _tokens[_index]; internal bool IsAtEnd => Current.Type == TokenType.End; internal Parser(List tokens) { _tokens = tokens; _index = 0; } internal bool TryParseExpression(Dictionary variables, out IFormulaNode? node, out string? error) { return TryParseAddSub(variables, out node, out error); } private bool TryParseAddSub(Dictionary 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 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 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 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 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 _variables = new Dictionary(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 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? tokens, out string? error) { tokens = new List(); 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? 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? _damageFormulaConfig; private ConfigEntry? _tumbleEnemyDamageFormulaConfig; private ConfigEntry? _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("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("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("Damage", "Debug", false, "Enable FormulaEngine startup self-tests logging."); _formulaEngine = CreateFormulaEngine(); _tumbleEnemyFormulaEngine = CreateFormulaEngine(); ReloadFormulaEngine(_damageFormulaConfig.Value, isRuntimeChange: false); ReloadTumbleEnemyFormulaEngine(_tumbleEnemyDamageFormulaConfig.Value, isRuntimeChange: false); ConfigEntry? 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); } } }