using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using GTFO.API; using GameData; using Gear; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Microsoft.CodeAnalysis; using ModifierAPI.Patches; using ModifierAPI.Structs; using Player; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("ModifierAPI")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+d2e8dac3df2b0771a167989c4ee9a102fe8fae8c")] [assembly: AssemblyProduct("ModifierAPI")] [assembly: AssemblyTitle("ModifierAPI")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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; } } } namespace ModifierAPI { internal static class DinoLogger { private static ManualLogSource logger = Logger.CreateLogSource("ModifierAPI"); public static void Log(string format, params object[] args) { Log(string.Format(format, args)); } public static void Log(string str) { if (logger != null) { logger.Log((LogLevel)8, (object)str); } } public static void Warning(string format, params object[] args) { Warning(string.Format(format, args)); } public static void Warning(string str) { if (logger != null) { logger.Log((LogLevel)4, (object)str); } } public static void Error(string format, params object[] args) { Error(string.Format(format, args)); } public static void Error(string str) { if (logger != null) { logger.Log((LogLevel)2, (object)str); } } public static void Debug(string format, params object[] args) { Debug(string.Format(format, args)); } public static void Debug(string str) { if (logger != null) { logger.Log((LogLevel)32, (object)str); } } } [BepInPlugin("Dinorush.ModifierAPI", "ModifierAPI", "1.3.2")] internal sealed class EntryPoint : BasePlugin { public const string MODNAME = "ModifierAPI"; public override void Load() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) new Harmony("ModifierAPI").PatchAll(); LevelAPI.OnLevelCleanup += MoveSpeedAPI.Reset; ((BasePlugin)this).Log.LogMessage((object)"Loaded ModifierAPI"); } } public interface IStatModifier { float Mod { get; set; } StackLayer Layer { get; } bool Active { get; } void Enable(); void Enable(float mod); void Disable(); } public static class MeleeAttackSpeedAPI { public const string DefaultGroup = "Default"; private static readonly Dictionary _groups = new Dictionary(); private static readonly Dictionary _animGroups = new Dictionary(); private static readonly Dictionary _lightGroups = new Dictionary(); private static readonly Dictionary _chargedGroups = new Dictionary(); private static readonly Dictionary _chargeSpeedGroups = new Dictionary(); private static readonly Dictionary _chargedAnimGroups = new Dictionary(); private static readonly Dictionary _pushGroups = new Dictionary(); private static float _mod = 1f; private static float _animMod = 1f; private static float _lightMod = 1f; private static float _chargedMod = 1f; private static float _chargeSpeedMod = 1f; private static float _chargedAnimMod = 1f; private static float _pushMod = 1f; private static MeleeWeaponFirstPerson? _cachedMelee; public static IStatModifier AddModifier(float mod, StackLayer layer = StackLayer.Multiply, string group = "Default") { return AddModifier(mod, layer, group, _groups); } public static IStatModifier AddAnimationModifier(float mod, StackLayer layer = StackLayer.Multiply, string group = "Default") { return AddModifier(mod, layer, group, _animGroups); } public static IStatModifier AddLightModifier(float mod, StackLayer layer = StackLayer.Multiply, string group = "Default") { return AddModifier(mod, layer, group, _lightGroups); } public static IStatModifier AddChargedModifier(float mod, StackLayer layer = StackLayer.Multiply, string group = "Default") { return AddModifier(mod, layer, group, _chargedGroups); } public static IStatModifier AddChargeSpeedModifier(float mod, StackLayer layer = StackLayer.Multiply, string group = "Default") { return AddModifier(mod, layer, group, _chargeSpeedGroups); } public static IStatModifier AddChargedAnimationModifier(float mod, StackLayer layer = StackLayer.Multiply, string group = "Default") { return AddModifier(mod, layer, group, _chargedAnimGroups); } public static IStatModifier AddPushModifier(float mod, StackLayer layer = StackLayer.Multiply, string group = "Default") { return AddModifier(mod, layer, group, _pushGroups); } private static IStatModifier AddModifier(float mod, StackLayer layer, string group, Dictionary groupSet) { if (layer < StackLayer.Multiply || (int)layer >= StackLayerConst.NumLayers) { throw new ArgumentException($"Invalid layer {layer} provided."); } if (!groupSet.TryGetValue(group, out ModifierGroup value)) { groupSet.Add(group, value = new ModifierGroup(delegate { Refresh(); })); } return value.Add(mod, layer); } internal static float GetMod(eMeleeWeaponState state) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected I4, but got Unknown float num; switch (state - 3) { case 0: case 1: case 2: case 3: num = _lightMod; break; case 4: case 5: case 6: case 7: case 8: case 9: num = _chargedMod * _chargedAnimMod; break; case 10: num = _pushMod; break; default: num = 1f; break; } return num * _mod * _animMod; } internal static float GetChargingMod() { return _chargedMod * _mod * _chargeSpeedMod; } internal static void ApplyToWeapon(MeleeWeaponFirstPerson melee) { _cachedMelee = melee; Refresh(force: true); } internal static void Reset() { _mod = 1f; _animMod = 1f; _lightMod = 1f; _chargedMod = 1f; _chargeSpeedMod = 1f; _chargedAnimMod = 1f; _pushMod = 1f; foreach (ModifierGroup value in _groups.Values) { value.Reset(); } foreach (ModifierGroup value2 in _animGroups.Values) { value2.Reset(); } foreach (ModifierGroup value3 in _lightGroups.Values) { value3.Reset(); } foreach (ModifierGroup value4 in _chargedGroups.Values) { value4.Reset(); } foreach (ModifierGroup value5 in _chargeSpeedGroups.Values) { value5.Reset(); } foreach (ModifierGroup value6 in _chargedAnimGroups.Values) { value6.Reset(); } foreach (ModifierGroup value7 in _pushGroups.Values) { value7.Reset(); } Refresh(force: true); } private static void Refresh(bool force = false) { bool flag = TryRefresh(ref _mod, _groups) || force; flag = TryRefresh(ref _animMod, _animGroups) || flag; bool flag2 = TryRefresh(ref _lightMod, _lightGroups) || flag; bool flag3 = TryRefresh(ref _chargedMod, _chargedGroups) || flag; flag3 = TryRefresh(ref _chargedAnimMod, _chargedAnimGroups) || flag3; flag3 = TryRefresh(ref _chargeSpeedMod, _chargeSpeedGroups) || flag3; bool flag4 = TryRefresh(ref _pushMod, _pushGroups) || flag; if (!((Object)(object)_cachedMelee == (Object)null)) { if (flag2) { SetLightAttackTimings(_cachedMelee); } if (flag3) { SetChargedAttackTimings(_cachedMelee); } if (flag4) { SetPushAttackTimings(_cachedMelee); } } } private static bool TryRefresh(ref float mod, Dictionary groups) { float num = 1f; foreach (ModifierGroup value in groups.Values) { num *= value.Mod.Value; } if (mod != num) { mod = num; return true; } return false; } private static void SetLightAttackTimings(MeleeWeaponFirstPerson melee) { float mod = 1f / (_mod * _animMod * _lightMod); Il2CppReferenceArray states = melee.m_states; MeleeAnimationSetDataBlock meleeAnimationData = ((ItemEquippable)melee).MeleeAnimationData; CopyMeleeData(((Il2CppArrayBase)(object)states)[3].AttackData, meleeAnimationData.FPAttackMissLeft, mod); CopyMeleeData(((Il2CppArrayBase)(object)states)[4].AttackData, meleeAnimationData.FPAttackMissRight, mod); CopyMeleeData(((Il2CppArrayBase)(object)states)[5].AttackData, meleeAnimationData.FPAttackHitLeft, mod); CopyMeleeData(((Il2CppArrayBase)(object)states)[6].AttackData, meleeAnimationData.FPAttackHitRight, mod); } private static void SetChargedAttackTimings(MeleeWeaponFirstPerson melee) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Invalid comparison between Unknown and I4 //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Invalid comparison between Unknown and I4 float mod = 1f / (_mod * _animMod * _chargedMod * _chargedAnimMod); Il2CppReferenceArray states = melee.m_states; MeleeAnimationSetDataBlock meleeAnimationData = ((ItemEquippable)melee).MeleeAnimationData; CopyMeleeData(((Il2CppArrayBase)(object)states)[8].AttackData, meleeAnimationData.FPAttackChargeUpReleaseLeft, mod); CopyMeleeData(((Il2CppArrayBase)(object)states)[10].AttackData, meleeAnimationData.FPAttackChargeUpReleaseRight, mod); CopyMeleeData(((Il2CppArrayBase)(object)states)[11].AttackData, meleeAnimationData.FPAttackChargeUpHitLeft, mod); CopyMeleeData(((Il2CppArrayBase)(object)states)[12].AttackData, meleeAnimationData.FPAttackChargeUpHitRight, mod); eMeleeWeaponState currentStateName = melee.CurrentStateName; if ((int)currentStateName == 7 || (int)currentStateName == 9) { MeleePatches.AdjustChargeTime(((Il2CppObjectBase)melee.CurrentState).Cast()); } } private static void SetPushAttackTimings(MeleeWeaponFirstPerson melee) { float mod = 1f / (_mod * _animMod * _pushMod); Il2CppReferenceArray states = melee.m_states; CopyMeleeData(animData: ((ItemEquippable)melee).MeleeAnimationData.FPAttackPush, data: ((Il2CppArrayBase)(object)states)[13].AttackData, mod: mod); } private static void CopyMeleeData(MeleeAttackData data, MeleeAttackData animData, float mod = 1f) { data.m_attackLength = animData.AttackLengthTime * mod; data.m_attackHitTime = animData.AttackHitTime * mod; data.m_damageStartTime = animData.DamageStartTime * mod; data.m_damageEndTime = animData.DamageEndTime * mod; data.m_attackCamFwdHitTime = animData.AttackCamFwdHitTime * mod; data.m_comboEarlyTime = animData.ComboEarlyTime * mod; } } public static class MeleeRangeAPI { public delegate bool RangeOverrideFunc(float baseRange, float mod); public const string DefaultGroup = "Default"; private static readonly Dictionary _groups = new Dictionary(); private static float _mod = 1f; private static float _baseRange = 0f; private static MeleeArchetypeDataBlock? _cachedBlock = null; private static RangeOverrideFunc? _rangeOverride = null; public static void SetRangeOverride(RangeOverrideFunc callback) { _rangeOverride = callback; } public static void RefreshRange() { SetRange(); } public static void SetBaseRange(float range) { if (_cachedBlock != null) { _cachedBlock.CameraDamageRayLength = (_baseRange = range); SetRange(); } } public static IStatModifier AddModifier(float mod, StackLayer layer = StackLayer.Multiply, string group = "Default") { return AddModifier(mod, layer, group, _groups); } private static IStatModifier AddModifier(float mod, StackLayer layer, string group, Dictionary groupSet) { if (layer < StackLayer.Multiply || (int)layer >= StackLayerConst.NumLayers) { throw new ArgumentException($"Invalid layer {layer} provided."); } if (!groupSet.TryGetValue(group, out ModifierGroup value)) { groupSet.Add(group, value = new ModifierGroup(delegate { Refresh(); })); } return value.Add(mod, layer); } internal static void ApplyToWeapon(MeleeWeaponFirstPerson melee) { if (_cachedBlock != null) { _cachedBlock.CameraDamageRayLength = _baseRange; } _cachedBlock = ((ItemEquippable)melee).MeleeArchetypeData; _baseRange = _cachedBlock.CameraDamageRayLength; Refresh(force: true); } internal static void Reset() { _mod = 1f; foreach (ModifierGroup value in _groups.Values) { value.Reset(); } Refresh(force: true); } private static void Refresh(bool force = false) { bool flag = TryRefresh(ref _mod, _groups) || force; if (_cachedBlock != null && flag) { SetRange(); } } private static bool TryRefresh(ref float mod, Dictionary groups) { float num = 1f; foreach (ModifierGroup value in groups.Values) { num *= value.Mod.Value; } if (mod != num) { mod = num; return true; } return false; } private static void SetRange() { if (_cachedBlock != null) { _cachedBlock.CameraDamageRayLength = _baseRange; if (_rangeOverride == null || _rangeOverride(_baseRange, _mod)) { MeleeArchetypeDataBlock? cachedBlock = _cachedBlock; cachedBlock.CameraDamageRayLength *= _mod; } } } } internal class ModifierGroup { public class StatModifier : IStatModifier { private float _mod; private readonly ModifierGroup _parent; public float Mod { get { return _mod; } set { float mod = _mod; _mod = value; if (Active && mod != _mod) { _parent.Refresh(Layer); } } } public StackLayer Layer { get; } public bool Active { get; internal set; } public StatModifier(float mod, StackLayer layer, ModifierGroup parent) { Active = false; _mod = mod; Layer = layer; _parent = parent; } public void Enable() { if (!Active) { Active = true; _parent.GetLayer(Layer).Add(this); _parent.Refresh(Layer); } } public void Enable(float mod) { Enable(); Mod = mod; } public void Disable() { if (Active) { Active = false; _parent.GetLayer(Layer).Remove(this); _parent.Refresh(Layer); } } } private readonly HashSet[] _layers = new HashSet[StackLayerConst.NumLayers]; private readonly Action _onRefresh; private StackValue _mod = new StackValue(); public StackValue Mod => _mod; public ModifierGroup(Action onRefresh) { _onRefresh = onRefresh; } public IStatModifier Add(float mod, StackLayer layer = StackLayer.Multiply) { StatModifier statModifier = new StatModifier(mod, layer, this); statModifier.Enable(); return statModifier; } public void Reset() { HashSet[] layers = _layers; foreach (HashSet hashSet in layers) { if (hashSet == null) { continue; } foreach (StatModifier item in hashSet) { item.Active = false; } hashSet.Clear(); } _mod.Reset(); } private HashSet GetLayer(StackLayer layer) { return _layers[(int)layer] ?? (_layers[(int)layer] = new HashSet()); } private void Refresh(StackLayer layer) { if (_layers[(int)layer] == null) { return; } _mod.Reset(layer); foreach (StatModifier item in GetLayer(layer)) { _mod.Add(item.Mod, layer); } _onRefresh(); } } public static class MoveSpeedAPI { public const string DefaultGroup = "Default"; private static readonly Dictionary _groups = new Dictionary(); private static readonly Dictionary _walkGroups = new Dictionary(); private static readonly Dictionary _crouchGroups = new Dictionary(); private static readonly Dictionary _sprintGroups = new Dictionary(); private static readonly Dictionary _airGroups = new Dictionary(); private static PlayerDataBlock _playerData = null; private static float _baseWalkSpeed; private static float _baseRunSpeed; private static float _baseCrouchSpeed; private static float _baseAirSpeed; public static IStatModifier AddModifier(float mod, StackLayer layer = StackLayer.Multiply, string group = "Default") { return AddModifier(_groups, mod, layer, group); } public static IStatModifier AddWalkModifier(float mod, StackLayer layer = StackLayer.Multiply, string group = "Default") { return AddModifier(_walkGroups, mod, layer, group); } public static IStatModifier AddSprintModifier(float mod, StackLayer layer = StackLayer.Multiply, string group = "Default") { return AddModifier(_sprintGroups, mod, layer, group); } public static IStatModifier AddCrouchModifier(float mod, StackLayer layer = StackLayer.Multiply, string group = "Default") { return AddModifier(_crouchGroups, mod, layer, group); } public static IStatModifier AddAirModifier(float mod, StackLayer layer = StackLayer.Multiply, string group = "Default") { return AddModifier(_airGroups, mod, layer, group); } private static IStatModifier AddModifier(Dictionary modGroup, float mod, StackLayer layer, string group) { if (layer < StackLayer.Multiply || (int)layer >= StackLayerConst.NumLayers) { throw new ArgumentException($"Invalid layer {layer} provided."); } if (!modGroup.TryGetValue(group, out ModifierGroup value)) { modGroup.Add(group, value = new ModifierGroup(Refresh)); } return value.Add(mod, layer); } internal static void Reset() { foreach (ModifierGroup value in _groups.Values) { value.Reset(); } foreach (ModifierGroup value2 in _walkGroups.Values) { value2.Reset(); } foreach (ModifierGroup value3 in _crouchGroups.Values) { value3.Reset(); } foreach (ModifierGroup value4 in _sprintGroups.Values) { value4.Reset(); } foreach (ModifierGroup value5 in _airGroups.Values) { value5.Reset(); } if (_playerData != null) { _playerData.walkMoveSpeed = _baseWalkSpeed; _playerData.runMoveSpeed = _baseRunSpeed; _playerData.crouchMoveSpeed = _baseCrouchSpeed; _playerData.airMoveSpeed = _baseAirSpeed; } } internal static void Refresh() { if (_playerData != null) { float mod = GetMod(_groups); _playerData.walkMoveSpeed = _baseWalkSpeed * mod * GetMod(_walkGroups); _playerData.runMoveSpeed = _baseRunSpeed * mod * GetMod(_sprintGroups); _playerData.crouchMoveSpeed = _baseCrouchSpeed * mod * GetMod(_crouchGroups); _playerData.airMoveSpeed = _baseAirSpeed * mod * GetMod(_airGroups); } } internal static void CachePlayerData(PlayerDataBlock data) { if (_playerData == null || !(((Il2CppObjectBase)_playerData).Pointer == ((Il2CppObjectBase)data).Pointer)) { _playerData = data; _baseWalkSpeed = _playerData.walkMoveSpeed; _baseRunSpeed = _playerData.runMoveSpeed; _baseCrouchSpeed = _playerData.crouchMoveSpeed; _baseAirSpeed = _playerData.airMoveSpeed; } } private static float GetMod(Dictionary modGroup) { float num = 1f; foreach (ModifierGroup value in modGroup.Values) { num *= value.Mod.Value; } return num; } } public enum StackLayer { Multiply, Add, Max, Min, Override } public static class StackLayerConst { public static readonly int NumLayers = (int)(Enum.GetValues()[^1] + 1); } } namespace ModifierAPI.Structs { internal struct StackValue { private float _value; private bool _isDirty; public bool UseOverride; public float OverrideMod; public float AddMod; public float MultMod; public float MaxMod; private bool _hasMax; public float MinMod; private bool _hasMin; public float Value { get { if (!_isDirty) { return _value; } _value = (UseOverride ? OverrideMod : (AddMod * MultMod * MaxMod * MinMod)); _isDirty = false; return _value; } } public StackValue() { UseOverride = false; OverrideMod = 1f; AddMod = 1f; MultMod = 1f; MaxMod = 1f; _hasMax = false; MinMod = 1f; _hasMin = false; _value = 1f; _isDirty = false; } public StackValue(StackValue other) { UseOverride = other.UseOverride; OverrideMod = other.OverrideMod; AddMod = other.AddMod; MultMod = other.MultMod; MaxMod = other.MaxMod; _hasMax = other._hasMax; MinMod = other.MinMod; _hasMin = other._hasMin; _value = other._value; _isDirty = other._isDirty; } public void Add(float mod, StackLayer type) { switch (type) { case StackLayer.Override: OverrideMod = mod; UseOverride = true; break; case StackLayer.Add: AddMod += mod - 1f; break; case StackLayer.Multiply: MultMod *= mod; break; case StackLayer.Max: MaxMod = Math.Max(GetMax(), mod); _hasMax = true; break; case StackLayer.Min: MinMod = Math.Min(GetMin(), mod); _hasMin = true; break; } _isDirty = true; } public void Reset(StackLayer type) { switch (type) { case StackLayer.Override: UseOverride = false; OverrideMod = 1f; break; case StackLayer.Add: AddMod = 1f; break; case StackLayer.Multiply: MultMod = 1f; break; case StackLayer.Max: MaxMod = 1f; _hasMax = false; break; case StackLayer.Min: MinMod = 1f; _hasMin = false; break; } _isDirty = true; } public void Reset() { UseOverride = false; OverrideMod = 1f; AddMod = 1f; MultMod = 1f; MaxMod = 1f; _hasMax = false; MinMod = 1f; _hasMin = false; _value = 1f; _isDirty = false; } public void Combine(StackValue other) { AddMod += other.AddMod - 1f; MultMod *= other.MultMod; MaxMod = ((_hasMax || other._hasMax) ? Math.Max(GetMax(), other.GetMax()) : 1f); _hasMax |= other._hasMax; MinMod = ((_hasMin || other._hasMin) ? Math.Min(GetMin(), other.GetMin()) : 1f); _hasMin |= other._hasMin; OverrideMod = (UseOverride ? OverrideMod : other.OverrideMod); UseOverride |= other.UseOverride; _isDirty = true; } private readonly float GetMin() { if (!_hasMin) { return float.MaxValue; } return MinMod; } private readonly float GetMax() { if (!_hasMax) { return float.MinValue; } return MaxMod; } } } namespace ModifierAPI.Patches { [HarmonyPatch(typeof(MeleeWeaponFirstPerson))] internal static class MeleePatches { private static MWS_AttackLight? _lightLeft; private static MWS_AttackLight? _lightRight; private static IntPtr _cachedPtr = IntPtr.Zero; private static float _cacheChargeDiff = -1f; private static float _lastMod = 1f; [HarmonyPatch("SetupMeleeAnimations")] [HarmonyWrapSafe] [HarmonyPriority(600)] [HarmonyPostfix] private static void Post_MeleeSetup(MeleeWeaponFirstPerson __instance) { MeleeAttackSpeedAPI.ApplyToWeapon(__instance); MeleeRangeAPI.ApplyToWeapon(__instance); } [HarmonyPatch("ChangeState")] [HarmonyWrapSafe] [HarmonyPostfix] private static void Post_MeleeChangeState(MeleeWeaponFirstPerson __instance, eMeleeWeaponState newState) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected I4, but got Unknown if (_cachedPtr != ((Il2CppObjectBase)__instance).Pointer) { _cachedPtr = ((Il2CppObjectBase)__instance).Pointer; _lightLeft = ((Il2CppObjectBase)((Il2CppArrayBase)(object)__instance.m_states)[3]).TryCast(); _lightRight = ((Il2CppObjectBase)((Il2CppArrayBase)(object)__instance.m_states)[4]).TryCast(); } float mod = MeleeAttackSpeedAPI.GetMod(newState); switch (newState - 3) { case 0: _lightLeft.m_wantedNormalSpeed = mod; _lightLeft.m_wantedChargeSpeed = mod * 0.3f; break; case 1: _lightRight.m_wantedNormalSpeed = mod; _lightRight.m_wantedChargeSpeed = mod * 0.3f; break; case 2: case 3: __instance.WeaponAnimator.speed = mod; break; case 5: case 7: __instance.WeaponAnimator.speed = mod; break; case 10: { Animator weaponAnimator = __instance.WeaponAnimator; weaponAnimator.speed *= mod; break; } case 4: case 6: case 8: case 9: break; } } [HarmonyPatch(typeof(MWS_ChargeUp), "Enter")] [HarmonyWrapSafe] [HarmonyPostfix] private static void ChargeCallback(MWS_ChargeUp __instance) { AdjustChargeTime(__instance); } internal static void AdjustChargeTime(MWS_ChargeUp chargeUp) { float chargingMod = MeleeAttackSpeedAPI.GetChargingMod(); if (_lastMod != chargingMod) { MeleeAnimationSetDataBlock meleeAnimationData = ((ItemEquippable)((MWS_Base)chargeUp).m_weapon).MeleeAnimationData; chargeUp.m_maxDamageTime *= _lastMod / chargingMod; chargeUp.m_startTime += chargeUp.m_elapsed / _lastMod - chargeUp.m_elapsed / chargingMod; if (_cacheChargeDiff != -1f) { meleeAnimationData.AutoAttackTime += _cacheChargeDiff; meleeAnimationData.AutoAttackWarningTime += _cacheChargeDiff; } _cacheChargeDiff = chargeUp.m_maxDamageTime * chargingMod - chargeUp.m_maxDamageTime; meleeAnimationData.AutoAttackTime -= _cacheChargeDiff; meleeAnimationData.AutoAttackWarningTime -= _cacheChargeDiff; } } [HarmonyPatch(typeof(MWS_ChargeUp), "Exit")] [HarmonyWrapSafe] [HarmonyPostfix] private static void RestoreAutoAttackTimings(MWS_ChargeUp __instance) { _lastMod = 1f; if (_cacheChargeDiff != -1f) { MeleeAnimationSetDataBlock meleeAnimationData = ((ItemEquippable)((MWS_Base)__instance).m_weapon).MeleeAnimationData; meleeAnimationData.AutoAttackTime += _cacheChargeDiff; meleeAnimationData.AutoAttackWarningTime += _cacheChargeDiff; _cacheChargeDiff = -1f; } } } [HarmonyPatch(typeof(LocalPlayerAgent))] internal static class PlayerSetupPatch { [HarmonyPatch("Setup")] [HarmonyPrefix] private static void OnAgentSetup(LocalPlayerAgent __instance) { if (!((PlayerAgent)__instance).m_isSetup) { MoveSpeedAPI.CachePlayerData(GameDataBlockBase.GetBlock(1u)); } } } }