using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; using UnityEngine.InputSystem; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Katana Dash Strike Debug")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Katana Dash Strike Debug")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("af2a21a3-46b6-4ff9-9bff-9c01c23a8b09")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace MeleeExpansion; [BepInPlugin("kumo.sulfur.melee_expansion", "The Dragonblade", "0.2.0")] public sealed class Plugin : BaseUnityPlugin { private sealed class ToggleState { public bool IsToggled; public bool AttackInProgress; public bool SheatheAfterAttack; public bool SuppressMeleeUntilReleased; public float NextChargeAttemptTime; } private sealed class SafeAnimatorState { public bool Valid; public int FullPathHash; public int ShortNameHash; public string ClipName; } private sealed class KatanaDashState { public bool IsDashing; public bool ForcedSprintActive; public float DashEndTime; public float NextDashTime; public Vector3 Direction; public Vector3 LastPosition; public object KatanaWeapon; public readonly HashSet HitUnitIds = new HashSet(); public bool PostHangActive; public float PostHangEndTime; public bool FallingEnabledWasSaved; public bool PreviousFallingEnabled = true; public float KillRefreshFeedbackEndTime; public float HealFeedbackEndTime; public float HealFeedbackAmount; } public const string PluginGuid = "kumo.sulfur.melee_expansion"; public const string PluginName = "The Dragonblade"; public const string PluginVersion = "0.2.0"; internal static ManualLogSource Log; internal static ConfigEntry EnableMod; internal static ConfigEntry FirePerformsMeleeAttack; internal static ConfigEntry ResetMeleeAnimatorBeforeSheathe; internal static ConfigEntry ReChargeRetryInterval; internal static ConfigEntry LogStateChanges; internal static ConfigEntry EnableKatanaDash; internal static ConfigEntry KatanaNameKeywords; internal static ConfigEntry DashCooldown; internal static ConfigEntry DashDistance; internal static ConfigEntry DashDuration; internal static ConfigEntry DashHitRadius; internal static ConfigEntry DashDamageMultiplier; internal static ConfigEntry DashHitEachUnitOnce; internal static ConfigEntry RefreshCooldownOnPlayerKill; internal static ConfigEntry RefreshRequiresKatanaStance; internal static ConfigEntry RefreshCooldownOnNonNpcUnitKill; internal static ConfigEntry HealOnEnemyKill; internal static ConfigEntry HealAmountOnEnemyKill; internal static ConfigEntry DashPostHangDuration; internal static ConfigEntry DashPostHangMaxDownwardSpeed; internal static ConfigEntry SuppressFallingAnimationDuringPostHang; internal static ConfigEntry EnableDashHud; internal static ConfigEntry DashIconFileName; internal static ConfigEntry DashHudIconSize; internal static ConfigEntry DashHudUseActualSprintBinding; internal static ConfigEntry DashHudFallbackKeyLabel; internal static ConfigEntry DashKillRefreshFeedbackDuration; internal static ConfigEntry DashHealFeedbackDuration; internal static ConfigEntry LogDash; private Harmony harmony; private static Type equipmentManagerType; private static Type weaponType; private static Type extendedWalkerType; private static Type moverType; private static Type playerType; private static Type unitType; private static Type npcType; private static Type entityStatsType; private static Type entityAttributesType; private static MethodInfo mHandleAimInput; private static MethodInfo mHandleMeleeInput; private static MethodInfo mPullTrigger; private static MethodInfo mReleaseTrigger; private static MethodInfo mChargeBasicMelee; private static MethodInfo mUseBasicMelee; private static MethodInfo mOnMeleeDone; private static MethodInfo mReportMeleeDone; private static MethodInfo mIsMeleeCharging; private static MethodInfo mChargeMelee; private static MethodInfo mSetAlternativeState; private static MethodInfo mUpdateSprinting; private static MethodInfo mToggleSprint; private static MethodInfo mMoverSetVelocity; private static MethodInfo mPlayerDirectionLooking; private static MethodInfo mWeaponGetDamage; private static MethodInfo mWeaponGetDamageType; private static MethodInfo mUnitReceiveDamageWithIDamager; private static MethodInfo mUnitReceiveDamageWithDamageSourceData; private static MethodInfo mEntityStatsModifyStatus; private static PropertyInfo pIsMelee; private static PropertyInfo pAnimator; private static PropertyInfo pSprintAction; private static PropertyInfo pFallingEnabled; private static PropertyInfo pSourceName; private static PropertyInfo pUnitState; private static PropertyInfo pUnitIsPlayer; private static PropertyInfo pUnitStats; private static PropertyInfo pNpcIsProtectedNpc; private static FieldInfo fAltFireAction; private static FieldInfo fMeleeFireAction; private static FieldInfo fMeleeFireActionAlternative; private static FieldInfo fCurrentHoldable; private static FieldInfo fEquipmentManager; private static FieldInfo fWalkerEquipmentManager; private static float LastActualHealAmount; private static FieldInfo fIsInMeleeCharge; private static FieldInfo fMeleePressed; private static FieldInfo fAlternativeMeleePressed; private static FieldInfo fAimingInputHeld; private static FieldInfo fMeleeInputCooldown; private static FieldInfo fCurrentParries; private static FieldInfo fWeaponDefinition; private static FieldInfo fUnitState; private static FieldInfo fUnitIsPlayer; private static FieldInfo fPlayerCamera; private static FieldInfo fDamageSourceDataIsPlayer; private static FieldInfo fDamageSourceDataSourceUnit; private static FieldInfo fDamageSourceDataSourceWeapon; private static Type hitmeshDataType; private static object hitmeshDataDefault; private static object entityAttributeStatusCurrentHealth; private static string PluginDirectory; private static Texture2D DashIconTexture; private static Texture2D HudPixelTexture; private static bool DashIconLoadAttempted; private static MethodInfo mEntityStatsGetStatus; private static MethodInfo mEntityStatsGetAttribute; private static object entityAttributeMaxHealth; private static readonly ConditionalWeakTable ToggleStates = new ConditionalWeakTable(); private static readonly ConditionalWeakTable SafeAnimatorStates = new ConditionalWeakTable(); private static readonly ConditionalWeakTable DashStates = new ConditionalWeakTable(); private static object lastKnownWalkerController; private void Awake() { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Expected O, but got Unknown //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Expected O, but got Unknown //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Expected O, but got Unknown //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Expected O, but got Unknown //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Expected O, but got Unknown //IL_0300: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Expected O, but got Unknown //IL_033d: Unknown result type (might be due to invalid IL or missing references) //IL_0347: Expected O, but got Unknown //IL_037a: Unknown result type (might be due to invalid IL or missing references) //IL_0384: Expected O, but got Unknown //IL_041b: Unknown result type (might be due to invalid IL or missing references) //IL_0425: Expected O, but got Unknown //IL_049c: Unknown result type (might be due to invalid IL or missing references) //IL_04a6: Expected O, but got Unknown //IL_04d9: Unknown result type (might be due to invalid IL or missing references) //IL_04e3: Expected O, but got Unknown //IL_0551: Unknown result type (might be due to invalid IL or missing references) //IL_055b: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; PluginDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); EnableMod = ((BaseUnityPlugin)this).Config.Bind("General", "EnableMod", true, "Enable Melee Expansion."); FirePerformsMeleeAttack = ((BaseUnityPlugin)this).Config.Bind("ToggleMelee", "FirePerformsMeleeAttack", true, "When melee stance is toggled on, pressing the normal Fire action performs one melee attack."); ReChargeRetryInterval = ((BaseUnityPlugin)this).Config.Bind("ToggleMelee", "ReChargeRetryInterval", 0.08f, new ConfigDescription("Seconds between retry attempts when the mod tries to re-enter melee charge after an attack.", (AcceptableValueBase)(object)new AcceptableValueRange(0.01f, 0.5f), Array.Empty())); ResetMeleeAnimatorBeforeSheathe = ((BaseUnityPlugin)this).Config.Bind("ToggleMelee", "ResetMeleeAnimatorBeforeSheathe", true, "Before sheathing a toggled melee weapon, reset its Animator to the cached safe equip state."); EnableKatanaDash = ((BaseUnityPlugin)this).Config.Bind("KatanaDash", "EnableKatanaDash", true, "Enable Katana Dash Strike."); KatanaNameKeywords = ((BaseUnityPlugin)this).Config.Bind("KatanaDash", "KatanaNameKeywords", "Katana,Wakizashi,BiggerKatana", "Comma-separated keywords used to detect katana-like melee weapons."); DashCooldown = ((BaseUnityPlugin)this).Config.Bind("KatanaDash", "Cooldown", 5f, new ConfigDescription("Dash cooldown in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 60f), Array.Empty())); DashDistance = ((BaseUnityPlugin)this).Config.Bind("KatanaDash", "Distance", 8f, new ConfigDescription("Dash distance.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 50f), Array.Empty())); DashDuration = ((BaseUnityPlugin)this).Config.Bind("KatanaDash", "Duration", 0.22f, new ConfigDescription("Dash duration in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange(0.03f, 2f), Array.Empty())); DashHitRadius = ((BaseUnityPlugin)this).Config.Bind("KatanaDash", "HitRadius", 1f, new ConfigDescription("Radius used to scan units along the dash path.", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 5f), Array.Empty())); DashDamageMultiplier = ((BaseUnityPlugin)this).Config.Bind("KatanaDash", "DamageMultiplier", 1f, new ConfigDescription("Dash damage multiplier applied to the current katana weapon damage.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 20f), Array.Empty())); DashHitEachUnitOnce = ((BaseUnityPlugin)this).Config.Bind("KatanaDash", "HitEachUnitOnce", true, "If true, each unit can only be damaged once per dash."); RefreshCooldownOnPlayerKill = ((BaseUnityPlugin)this).Config.Bind("KatanaDash", "RefreshCooldownOnPlayerKill", true, "Refresh Katana Dash cooldown when the player kills an enemy."); RefreshRequiresKatanaStance = ((BaseUnityPlugin)this).Config.Bind("KatanaDash", "RefreshRequiresKatanaStance", true, "If true, kill refresh only works while the player is in toggled katana stance."); RefreshCooldownOnNonNpcUnitKill = ((BaseUnityPlugin)this).Config.Bind("KatanaDash", "RefreshCooldownOnNonNpcUnitKill", false, "If true, killing non-NPC Units such as breakable obstacles can also refresh Katana Dash cooldown. Healing never triggers from non-NPC Units."); HealOnEnemyKill = ((BaseUnityPlugin)this).Config.Bind("KatanaDash", "HealOnEnemyKill", true, "Heal the player when killing an enemy NPC while in katana stance. This never triggers from breakable obstacles."); HealAmountOnEnemyKill = ((BaseUnityPlugin)this).Config.Bind("KatanaDash", "HealAmountOnEnemyKill", 5f, new ConfigDescription("Health restored when killing an enemy NPC.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 999f), Array.Empty())); DashPostHangDuration = ((BaseUnityPlugin)this).Config.Bind("KatanaDash", "PostHangDuration", 0.12f, new ConfigDescription("Short hang time after Katana Dash ends. This prevents the player from immediately dropping at full falling speed.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 0.5f), Array.Empty())); DashPostHangMaxDownwardSpeed = ((BaseUnityPlugin)this).Config.Bind("KatanaDash", "PostHangMaxDownwardSpeed", 0.5f, new ConfigDescription("Maximum downward speed during post-dash hang. 0 means no downward velocity during hang.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 20f), Array.Empty())); SuppressFallingAnimationDuringPostHang = ((BaseUnityPlugin)this).Config.Bind("KatanaDash", "SuppressFallingAnimationDuringPostHang", true, "Temporarily suppress the Falling animation during post-dash hang."); EnableDashHud = ((BaseUnityPlugin)this).Config.Bind("UI", "EnableDashHud", true, "Show a bottom-right Katana Dash cooldown HUD."); DashIconFileName = ((BaseUnityPlugin)this).Config.Bind("UI", "DashIconFileName", "dash_icon.png", "PNG file name for the Katana Dash HUD icon. Place it next to MeleeExpansion.dll."); DashHudIconSize = ((BaseUnityPlugin)this).Config.Bind("UI", "DashHudIconSize", 76f, new ConfigDescription("Dash HUD icon size in pixels.", (AcceptableValueBase)(object)new AcceptableValueRange(48f, 128f), Array.Empty())); DashHudUseActualSprintBinding = ((BaseUnityPlugin)this).Config.Bind("UI", "DashHudUseActualSprintBinding", true, "Show the actual Sprint binding from the game's InputAction when possible."); DashHudFallbackKeyLabel = ((BaseUnityPlugin)this).Config.Bind("UI", "DashHudFallbackKeyLabel", "SHIFT", "Fallback key label shown on the Dash HUD."); DashKillRefreshFeedbackDuration = ((BaseUnityPlugin)this).Config.Bind("UI", "DashKillRefreshFeedbackDuration", 0.45f, new ConfigDescription("Seconds to flash the Dash HUD when a kill refreshes the cooldown.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 3f), Array.Empty())); DashHealFeedbackDuration = ((BaseUnityPlugin)this).Config.Bind("UI", "DashHealFeedbackDuration", 0.85f, new ConfigDescription("Seconds to show +HP feedback on enemy kill.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 3f), Array.Empty())); LogStateChanges = ((BaseUnityPlugin)this).Config.Bind("Debug", "LogStateChanges", false, "Log toggle melee state changes."); LogDash = ((BaseUnityPlugin)this).Config.Bind("Debug", "LogDash", false, "Log Katana Dash debug events."); if (!InitializeReflection()) { ((BaseUnityPlugin)this).Logger.LogError((object)"Reflection initialization failed. Mod will not patch."); return; } harmony = new Harmony("kumo.sulfur.melee_expansion"); Patch(mHandleAimInput, "HandleAimInputPrefix"); Patch(mHandleMeleeInput, "HandleMeleeInputPrefix"); Patch(mPullTrigger, "PullTriggerPrefix"); Patch(mReleaseTrigger, "ReleaseTriggerPrefix"); Patch(mReportMeleeDone, "ReportMeleeDonePrefix", "ReportMeleeDonePostfix"); Patch(mChargeMelee, null, "ChargeMeleePostfix"); Patch(mUpdateSprinting, "UpdateSprintingPrefix"); Patch(mMoverSetVelocity, "MoverSetVelocityPrefix"); Patch(mUnitReceiveDamageWithDamageSourceData, "UnitReceiveDamageDamageSourcePrefix", "UnitReceiveDamageDamageSourcePostfix"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Melee Expansion loaded."); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } private void OnGUI() { if (!IsModActive() || EnableDashHud == null || !EnableDashHud.Value || lastKnownWalkerController == null) { return; } object equipmentManagerFromWalker = GetEquipmentManagerFromWalker(lastKnownWalkerController); if (equipmentManagerFromWalker != null) { ToggleState toggleState = GetToggleState(equipmentManagerFromWalker); if (IsKatanaDashHudContext(lastKnownWalkerController, equipmentManagerFromWalker, toggleState, out var _)) { KatanaDashState dashState = GetDashState(lastKnownWalkerController); object katana2; bool canUseDashNow = IsKatanaDashContext(lastKnownWalkerController, equipmentManagerFromWalker, toggleState, out katana2); DrawKatanaDashHud(dashState, canUseDashNow); } } } private static bool InitializeReflection() { equipmentManagerType = AccessTools.TypeByName("PerfectRandom.Sulfur.Core.Items.EquipmentManager"); weaponType = AccessTools.TypeByName("PerfectRandom.Sulfur.Core.Weapons.Weapon"); extendedWalkerType = AccessTools.TypeByName("PerfectRandom.Sulfur.Core.Movement.ExtendedAdvancedWalkerController"); moverType = AccessTools.TypeByName("CMF.Mover"); playerType = AccessTools.TypeByName("PerfectRandom.Sulfur.Core.Units.Player"); unitType = AccessTools.TypeByName("PerfectRandom.Sulfur.Core.Units.Unit"); npcType = AccessTools.TypeByName("PerfectRandom.Sulfur.Core.Units.Npc"); entityStatsType = AccessTools.TypeByName("PerfectRandom.Sulfur.Core.Stats.EntityStats"); entityAttributesType = AccessTools.TypeByName("PerfectRandom.Sulfur.Core.Stats.EntityAttributes"); if (equipmentManagerType == null) { ManualLogSource log = Log; if (log != null) { log.LogError((object)"Could not find EquipmentManager type."); } return false; } if (weaponType == null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogError((object)"Could not find Weapon type."); } return false; } if (extendedWalkerType == null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogError((object)"Could not find ExtendedAdvancedWalkerController type."); } return false; } if (moverType == null) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogError((object)"Could not find CMF.Mover type."); } return false; } if (playerType == null) { ManualLogSource log5 = Log; if (log5 != null) { log5.LogError((object)"Could not find Player type."); } return false; } if (unitType == null) { ManualLogSource log6 = Log; if (log6 != null) { log6.LogError((object)"Could not find Unit type."); } return false; } if (entityStatsType == null) { ManualLogSource log7 = Log; if (log7 != null) { log7.LogError((object)"Could not find EntityStats type."); } return false; } if (entityAttributesType == null) { ManualLogSource log8 = Log; if (log8 != null) { log8.LogError((object)"Could not find EntityAttributes type."); } return false; } try { entityAttributeStatusCurrentHealth = Enum.Parse(entityAttributesType, "Status_CurrentHealth"); entityAttributeMaxHealth = TryParseEntityAttribute("Stat_MaxHealth", "Status_MaxHealth", "MaxHealth", "HealthMax", "Stat_HealthMax"); if (entityAttributeMaxHealth == null) { ManualLogSource log9 = Log; if (log9 != null) { log9.LogError((object)"Could not resolve max health EntityAttributes value."); } return false; } } catch (Exception ex) { ManualLogSource log10 = Log; if (log10 != null) { log10.LogError((object)("Could not parse EntityAttributes.Status_CurrentHealth: " + ex)); } return false; } mHandleAimInput = AccessTools.Method(equipmentManagerType, "HandleAimInput", new Type[1] { typeof(bool) }, (Type[])null); mHandleMeleeInput = AccessTools.Method(equipmentManagerType, "HandleMeleeInput", new Type[1] { typeof(bool) }, (Type[])null); mPullTrigger = AccessTools.Method(equipmentManagerType, "PullTrigger", (Type[])null, (Type[])null); mReleaseTrigger = AccessTools.Method(equipmentManagerType, "ReleaseTrigger", (Type[])null, (Type[])null); mChargeBasicMelee = AccessTools.Method(equipmentManagerType, "ChargeBasicMelee", (Type[])null, (Type[])null); mUseBasicMelee = AccessTools.Method(equipmentManagerType, "UseBasicMelee", (Type[])null, (Type[])null); mOnMeleeDone = AccessTools.Method(equipmentManagerType, "OnMeleeDone", (Type[])null, (Type[])null); mReportMeleeDone = AccessTools.Method(weaponType, "ReportMeleeDone", (Type[])null, (Type[])null); mIsMeleeCharging = AccessTools.Method(weaponType, "IsMeleeCharging", (Type[])null, (Type[])null); mChargeMelee = AccessTools.Method(weaponType, "ChargeMelee", new Type[1] { typeof(bool) }, (Type[])null); mSetAlternativeState = AccessTools.Method(weaponType, "SetAlternativeState", new Type[1] { typeof(int) }, (Type[])null); mWeaponGetDamage = AccessTools.Method(weaponType, "GetDamage", (Type[])null, (Type[])null); mWeaponGetDamageType = AccessTools.Method(weaponType, "GetDamageType", (Type[])null, (Type[])null); mUpdateSprinting = AccessTools.Method(extendedWalkerType, "UpdateSprinting", (Type[])null, (Type[])null); mToggleSprint = AccessTools.Method(extendedWalkerType, "ToggleSprint", new Type[1] { typeof(bool) }, (Type[])null); mMoverSetVelocity = AccessTools.Method(moverType, "SetVelocity", new Type[1] { typeof(Vector3) }, (Type[])null); mPlayerDirectionLooking = AccessTools.Method(playerType, "DirectionLooking", new Type[1] { typeof(bool) }, (Type[])null); mUnitReceiveDamageWithIDamager = FindUnitReceiveDamageWithIDamager(); mUnitReceiveDamageWithDamageSourceData = FindUnitReceiveDamageWithDamageSourceData(); if (mUnitReceiveDamageWithIDamager != null) { ParameterInfo[] parameters = mUnitReceiveDamageWithIDamager.GetParameters(); hitmeshDataType = parameters[3].ParameterType; hitmeshDataDefault = GetHitmeshDataDefault(hitmeshDataType); } if (mUnitReceiveDamageWithDamageSourceData != null) { Type parameterType = mUnitReceiveDamageWithDamageSourceData.GetParameters()[2].ParameterType; fDamageSourceDataIsPlayer = AccessTools.Field(parameterType, "isPlayer"); fDamageSourceDataSourceUnit = AccessTools.Field(parameterType, "sourceUnit"); fDamageSourceDataSourceWeapon = AccessTools.Field(parameterType, "sourceWeapon"); } pIsMelee = AccessTools.Property(weaponType, "IsMelee"); pAnimator = FindPropertyRecursive(weaponType, "Animator"); pSprintAction = FindPropertyRecursive(extendedWalkerType, "sprintAction"); pFallingEnabled = FindPropertyRecursive(extendedWalkerType, "fallingEnabled"); pSourceName = FindPropertyRecursive(weaponType, "SourceName"); pUnitState = FindPropertyRecursive(unitType, "UnitState"); pUnitIsPlayer = FindPropertyRecursive(unitType, "isPlayer"); pUnitStats = FindPropertyRecursive(unitType, "Stats"); if (npcType != null) { pNpcIsProtectedNpc = FindPropertyRecursive(npcType, "IsProtectedNpc"); } mEntityStatsModifyStatus = AccessTools.Method(entityStatsType, "ModifyStatus", new Type[3] { entityAttributesType, typeof(float), typeof(bool) }, (Type[])null); mEntityStatsGetStatus = AccessTools.Method(entityStatsType, "GetStatus", new Type[1] { entityAttributesType }, (Type[])null); mEntityStatsGetAttribute = AccessTools.Method(entityStatsType, "GetAttribute", new Type[1] { entityAttributesType }, (Type[])null); fAltFireAction = FindFieldRecursive(equipmentManagerType, "altFireAction"); fMeleeFireAction = FindFieldRecursive(equipmentManagerType, "meleeFireAction"); fMeleeFireActionAlternative = FindFieldRecursive(equipmentManagerType, "meleeFireActionAlternative"); fCurrentHoldable = FindFieldRecursive(equipmentManagerType, "currentHoldable"); fEquipmentManager = FindFieldRecursive(weaponType, "equipmentManager"); fWalkerEquipmentManager = FindFieldRecursive(extendedWalkerType, "equipmentManager"); fIsInMeleeCharge = FindFieldRecursive(equipmentManagerType, "k__BackingField", "isInMeleeCharge"); fMeleePressed = FindFieldRecursive(equipmentManagerType, "k__BackingField", "meleePressed"); fAlternativeMeleePressed = FindFieldRecursive(equipmentManagerType, "k__BackingField", "alternativeMeleePressed"); fAimingInputHeld = FindFieldRecursive(equipmentManagerType, "k__BackingField", "AimingInputHeld"); fMeleeInputCooldown = FindFieldRecursive(equipmentManagerType, "meleeInputCooldown"); fCurrentParries = FindFieldRecursive(weaponType, "currentParries"); fWeaponDefinition = FindFieldRecursive(weaponType, "weaponDefinition", "k__BackingField"); fUnitState = FindFieldRecursive(unitType, "UnitState", "k__BackingField"); fUnitIsPlayer = FindFieldRecursive(unitType, "isPlayer", "k__BackingField"); fPlayerCamera = FindFieldRecursive(playerType, "playerCamera"); return Require(mHandleAimInput, "EquipmentManager.HandleAimInput(bool)") && Require(mHandleMeleeInput, "EquipmentManager.HandleMeleeInput(bool)") && Require(mPullTrigger, "EquipmentManager.PullTrigger()") && Require(mReleaseTrigger, "EquipmentManager.ReleaseTrigger()") && Require(mChargeBasicMelee, "EquipmentManager.ChargeBasicMelee()") && Require(mUseBasicMelee, "EquipmentManager.UseBasicMelee()") && Require(mOnMeleeDone, "EquipmentManager.OnMeleeDone()") && Require(mReportMeleeDone, "Weapon.ReportMeleeDone()") && Require(mIsMeleeCharging, "Weapon.IsMeleeCharging()") && Require(mChargeMelee, "Weapon.ChargeMelee(bool)") && Require(mSetAlternativeState, "Weapon.SetAlternativeState(int)") && Require(mWeaponGetDamage, "Weapon.GetDamage()") && Require(entityAttributeMaxHealth, "EntityAttributes max health") && Require(mEntityStatsGetStatus, "EntityStats.GetStatus(EntityAttributes)") && Require(mWeaponGetDamageType, "Weapon.GetDamageType()") && Require(mUpdateSprinting, "ExtendedAdvancedWalkerController.UpdateSprinting()") && Require(mToggleSprint, "ExtendedAdvancedWalkerController.ToggleSprint(bool)") && Require(mMoverSetVelocity, "Mover.SetVelocity(Vector3)") && Require(mUnitReceiveDamageWithIDamager, "Unit.ReceiveDamage(float, DamageTypes, IDamager, Hitmesh.Data, Vector3?)") && Require(mUnitReceiveDamageWithDamageSourceData, "Unit.ReceiveDamage(float, DamageTypes, DamageSourceData, Hitmesh.Data, Vector3?)") && Require(hitmeshDataType, "Hitmesh.Data parameter type") && Require(hitmeshDataDefault, "Hitmesh.Data.Default") && Require(fDamageSourceDataIsPlayer, "DamageSourceData.isPlayer") && Require(fDamageSourceDataSourceUnit, "DamageSourceData.sourceUnit") && Require(pIsMelee, "Weapon.IsMelee") && Require(pAnimator, "Holdable.Animator") && Require(pSprintAction, "ExtendedAdvancedWalkerController.sprintAction") && Require(pUnitStats, "Unit.Stats") && Require(mEntityStatsModifyStatus, "EntityStats.ModifyStatus(EntityAttributes, float, bool)") && Require(entityAttributeStatusCurrentHealth, "EntityAttributes.Status_CurrentHealth") && Require(fAltFireAction, "EquipmentManager.altFireAction") && Require(fMeleeFireAction, "EquipmentManager.meleeFireAction") && Require(fMeleeFireActionAlternative, "EquipmentManager.meleeFireActionAlternative") && Require(fCurrentHoldable, "EquipmentManager.currentHoldable") && Require(fEquipmentManager, "Weapon.equipmentManager") && Require(fWalkerEquipmentManager, "ExtendedAdvancedWalkerController.equipmentManager") && Require(fIsInMeleeCharge, "EquipmentManager.isInMeleeCharge") && Require(fMeleePressed, "EquipmentManager.meleePressed") && Require(fAlternativeMeleePressed, "EquipmentManager.alternativeMeleePressed") && Require(fAimingInputHeld, "EquipmentManager.AimingInputHeld") && Require(fMeleeInputCooldown, "EquipmentManager.meleeInputCooldown"); } private static bool Require(object member, string name) { if (member != null) { return true; } ManualLogSource log = Log; if (log != null) { log.LogError((object)("Missing required member: " + name)); } return false; } private void Patch(MethodInfo original, string prefix = null, string postfix = null) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown HarmonyMethod val = null; HarmonyMethod val2 = null; if (!string.IsNullOrEmpty(prefix)) { val = new HarmonyMethod(AccessTools.Method(typeof(Plugin), prefix, (Type[])null, (Type[])null)); } if (!string.IsNullOrEmpty(postfix)) { val2 = new HarmonyMethod(AccessTools.Method(typeof(Plugin), postfix, (Type[])null, (Type[])null)); } harmony.Patch((MethodBase)original, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static MethodInfo FindUnitReceiveDamageWithIDamager() { MethodInfo[] methods = unitType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo.Name != "ReceiveDamage")) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 5 && !(parameters[0].ParameterType != typeof(float)) && !(parameters[2].ParameterType.Name != "IDamager") && !(parameters[4].ParameterType != typeof(Vector3?))) { return methodInfo; } } } return null; } private static MethodInfo FindUnitReceiveDamageWithDamageSourceData() { MethodInfo[] methods = unitType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo.Name != "ReceiveDamage")) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 5 && !(parameters[0].ParameterType != typeof(float)) && !(parameters[2].ParameterType.Name != "DamageSourceData") && !(parameters[4].ParameterType != typeof(Vector3?))) { return methodInfo; } } } return null; } private static object GetHitmeshDataDefault(Type type) { if (type == null) { return null; } FieldInfo fieldInfo = AccessTools.Field(type, "Default"); if (fieldInfo != null) { return fieldInfo.GetValue(null); } PropertyInfo propertyInfo = AccessTools.Property(type, "Default"); if (propertyInfo != null) { return propertyInfo.GetValue(null, null); } try { return Activator.CreateInstance(type); } catch { return null; } } private static FieldInfo FindFieldRecursive(Type type, params string[] names) { Type type2 = type; while (type2 != null) { foreach (string text in names) { FieldInfo fieldInfo = AccessTools.Field(type2, text); if (fieldInfo != null) { return fieldInfo; } } type2 = type2.BaseType; } return null; } private static PropertyInfo FindPropertyRecursive(Type type, string name) { Type type2 = type; while (type2 != null) { PropertyInfo propertyInfo = AccessTools.Property(type2, name); if (propertyInfo != null) { return propertyInfo; } type2 = type2.BaseType; } return null; } private static bool IsModActive() { return EnableMod != null && EnableMod.Value; } private static ToggleState GetToggleState(object equipmentManager) { return ToggleStates.GetValue(equipmentManager, (object _) => new ToggleState()); } private static KatanaDashState GetDashState(object walkerController) { return DashStates.GetValue(walkerController, (object _) => new KatanaDashState()); } private static void HandleAimInputPrefix(object __instance, ref bool holdingMeleeAction) { if (IsModActive()) { ToggleState toggleState = GetToggleState(__instance); if (toggleState.IsToggled) { holdingMeleeAction = true; } } } private static bool HandleMeleeInputPrefix(object __instance, bool holdingMeleeAction) { if (!IsModActive()) { return true; } ToggleState toggleState = GetToggleState(__instance); if (toggleState.SuppressMeleeUntilReleased) { if (IsMeleeHeld(__instance)) { return false; } toggleState.SuppressMeleeUntilReleased = false; if (LogStateChanges.Value) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"Melee input released after manual sheathe. Input suppression cleared."); } } return false; } bool flag = WasMeleePressedThisFrame(__instance); if (!toggleState.IsToggled && IsAimingInputHeld(__instance) && holdingMeleeAction) { return true; } if (!toggleState.IsToggled && IsMeleeInputCoolingDown(__instance)) { return true; } if (flag) { if (!toggleState.IsToggled) { ToggleOn(__instance, toggleState); return false; } ToggleOff(__instance, toggleState); return false; } if (toggleState.IsToggled) { MaintainToggledMelee(__instance, toggleState); return false; } return true; } private static bool PullTriggerPrefix(object __instance) { if (!IsModActive()) { return true; } ToggleState toggleState = GetToggleState(__instance); if (!toggleState.IsToggled) { return true; } if (!FirePerformsMeleeAttack.Value) { return false; } object currentHoldable = GetCurrentHoldable(__instance); if (!IsMeleeWeapon(currentHoldable)) { MaintainToggledMelee(__instance, toggleState); return false; } if (!IsMeleeCharging(currentHoldable)) { MaintainToggledMelee(__instance, toggleState); return false; } if (toggleState.AttackInProgress) { return false; } toggleState.AttackInProgress = true; toggleState.SheatheAfterAttack = false; toggleState.SuppressMeleeUntilReleased = false; if (LogStateChanges.Value) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"Fire action converted to melee attack."); } } InvokeUseBasicMelee(__instance); return false; } private static bool ReleaseTriggerPrefix(object __instance) { if (!IsModActive()) { return true; } ToggleState toggleState = GetToggleState(__instance); if (!toggleState.IsToggled && !toggleState.AttackInProgress && !toggleState.SheatheAfterAttack) { return true; } return false; } private static void ReportMeleeDonePrefix(object __instance) { if (!IsModActive()) { return; } object equipmentManagerFromWeapon = GetEquipmentManagerFromWeapon(__instance); if (equipmentManagerFromWeapon != null) { ToggleState toggleState = GetToggleState(equipmentManagerFromWeapon); if (toggleState.IsToggled || toggleState.AttackInProgress || toggleState.SheatheAfterAttack) { PrepareMeleeAnimatorForFutureDraw(__instance); } } } private static void ReportMeleeDonePostfix(object __instance) { if (!IsModActive()) { return; } object equipmentManagerFromWeapon = GetEquipmentManagerFromWeapon(__instance); if (equipmentManagerFromWeapon == null) { return; } ToggleState toggleState = GetToggleState(equipmentManagerFromWeapon); if (toggleState.SheatheAfterAttack) { toggleState.AttackInProgress = false; toggleState.SheatheAfterAttack = false; toggleState.IsToggled = false; toggleState.SuppressMeleeUntilReleased = true; toggleState.NextChargeAttemptTime = 0f; if (LogStateChanges.Value) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"Melee attack finished. Staying sheathed."); } } return; } if (!toggleState.IsToggled) { toggleState.AttackInProgress = false; toggleState.NextChargeAttemptTime = 0f; return; } toggleState.AttackInProgress = false; toggleState.NextChargeAttemptTime = Time.time + ClampRetryInterval(); if (LogStateChanges.Value) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)"Melee attack finished. Re-entering toggled melee stance."); } } InvokeChargeBasicMelee(equipmentManagerFromWeapon); } private static void ChargeMeleePostfix(object __instance, bool state) { if (IsModActive() && state) { CacheSafeMeleeAnimatorStateIfUseful(__instance, "ChargeMelee(true)"); } } private static bool UpdateSprintingPrefix(object __instance) { if (!IsModActive() || !EnableKatanaDash.Value) { return true; } KatanaDashState dashState = GetDashState(__instance); object equipmentManagerFromWalker = GetEquipmentManagerFromWalker(__instance); if (equipmentManagerFromWalker == null) { return true; } ToggleState toggleState = GetToggleState(equipmentManagerFromWalker); if (!IsKatanaDashContext(__instance, equipmentManagerFromWalker, toggleState, out var katana)) { dashState.ForcedSprintActive = false; return true; } lastKnownWalkerController = __instance; InputAction sprintAction = GetSprintAction(__instance); if (WasPerformedThisFrame(sprintAction)) { TryStartKatanaDash(__instance, equipmentManagerFromWalker, katana); } dashState.ForcedSprintActive = true; InvokeToggleSprint(__instance, state: true); return false; } private static void MoverSetVelocityPrefix(object __instance, ref Vector3 _velocity) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) if (!IsModActive() || !EnableKatanaDash.Value) { return; } Component val = (Component)((__instance is Component) ? __instance : null); if ((Object)(object)val == (Object)null) { return; } Component component = val.GetComponent(extendedWalkerType); if ((Object)(object)component == (Object)null) { return; } KatanaDashState dashState = GetDashState(component); if (dashState.IsDashing) { UpdateKatanaDash(component, dashState); if (dashState.IsDashing) { float dashSpeed = GetDashSpeed(); _velocity = dashState.Direction * dashSpeed; return; } } if (dashState.PostHangActive) { ApplyPostDashHangVelocity(component, dashState, ref _velocity); } } private static void UnitReceiveDamageDamageSourcePrefix(object __instance, ref bool __state) { __state = false; if (IsModActive() && RefreshCooldownOnPlayerKill != null && RefreshCooldownOnPlayerKill.Value && __instance != null && !IsDeadUnit(__instance) && IsKilledUnitEligibleForDashRefresh(__instance)) { __state = true; } } private static void UnitReceiveDamageDamageSourcePostfix(object __instance, bool __state, object[] __args) { if (__state && __instance != null && IsDeadUnit(__instance) && IsKilledUnitEligibleForDashRefresh(__instance) && __args != null && __args.Length >= 3) { object damageSourceData = __args[2]; if (IsPlayerDamageSource(damageSourceData, out var sourceUnit)) { RefreshKatanaDashCooldownAndHealForKill(sourceUnit, __instance); } } } private static bool IsPlayerDamageSource(object damageSourceData, out object sourceUnit) { sourceUnit = null; if (damageSourceData == null) { return false; } try { if (!(fDamageSourceDataIsPlayer.GetValue(damageSourceData) is int num) || num == 0) { return false; } if (fDamageSourceDataSourceUnit != null) { sourceUnit = fDamageSourceDataSourceUnit.GetValue(damageSourceData); } return true; } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogError((object)("Failed to read DamageSourceData: " + ex)); } return false; } } private static void RefreshKatanaDashCooldownAndHealForKill(object sourceUnit, object killedUnit) { object obj = null; Component val = (Component)((sourceUnit is Component) ? sourceUnit : null); if ((Object)(object)val != (Object)null) { obj = val.GetComponent(extendedWalkerType); } if (obj == null) { obj = lastKnownWalkerController; } if (obj == null) { return; } object equipmentManagerFromWalker = GetEquipmentManagerFromWalker(obj); if (equipmentManagerFromWalker == null) { return; } ToggleState toggleState = GetToggleState(equipmentManagerFromWalker); if (RefreshRequiresKatanaStance != null && RefreshRequiresKatanaStance.Value && !IsKatanaDashHudContext(obj, equipmentManagerFromWalker, toggleState, out var _)) { return; } KatanaDashState dashState = GetDashState(obj); float num = Mathf.Max(0f, dashState.NextDashTime - Time.time); dashState.NextDashTime = Time.time; dashState.KillRefreshFeedbackEndTime = Time.time + GetDashKillRefreshFeedbackDuration(); LastActualHealAmount = 0f; bool flag = TryHealPlayerFromEnemyKill(sourceUnit, killedUnit); if (flag && LastActualHealAmount > 0f) { dashState.HealFeedbackAmount = LastActualHealAmount; dashState.HealFeedbackEndTime = Time.time + GetDashHealFeedbackDuration(); } if (LogDash != null && LogDash.Value) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("Katana Dash cooldown refreshed by kill. Previous cooldown left: " + num.ToString("F2") + "s healed=" + flag + " killedUnit=" + killedUnit)); } } } private static bool TryHealPlayerFromEnemyKill(object sourceUnit, object killedUnit) { if (HealOnEnemyKill == null || !HealOnEnemyKill.Value) { return false; } float healAmountOnEnemyKill = GetHealAmountOnEnemyKill(); if (healAmountOnEnemyKill <= 0f) { return false; } if (!HasNpcComponent(killedUnit)) { return false; } if (IsPlayerUnit(killedUnit)) { return false; } if (IsProtectedNpc(killedUnit)) { return false; } if (sourceUnit == null) { return false; } if (!IsPlayerUnit(sourceUnit)) { return false; } return HealUnit(sourceUnit, healAmountOnEnemyKill); } private static bool HealUnit(object unit, float amount) { if (unit == null || amount <= 0f) { return false; } try { object obj = null; if (pUnitStats != null) { obj = pUnitStats.GetValue(unit, null); } if (obj == null) { return false; } float entityStatValue = GetEntityStatValue(obj, entityAttributeStatusCurrentHealth, preferStatus: true); float entityStatValue2 = GetEntityStatValue(obj, entityAttributeMaxHealth, preferStatus: false); if (entityStatValue2 <= 0f) { return false; } float num = Mathf.Max(0f, entityStatValue2 - entityStatValue); float num2 = Mathf.Min(amount, num); if (num2 <= 0f) { return false; } mEntityStatsModifyStatus.Invoke(obj, new object[3] { entityAttributeStatusCurrentHealth, num2, false }); if (LogDash != null && LogDash.Value) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("Healed player for " + num2.ToString("F1") + " on enemy kill. Current=" + entityStatValue.ToString("F1") + " Max=" + entityStatValue2.ToString("F1"))); } } LastActualHealAmount = num2; return true; } catch (Exception ex) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogError((object)("Failed to heal player on enemy kill: " + ex)); } return false; } } private static bool IsKilledUnitEligibleForDashRefresh(object unit) { if (unit == null) { return false; } if (IsPlayerUnit(unit)) { return false; } if (IsProtectedNpc(unit)) { return false; } if (HasNpcComponent(unit)) { return true; } return RefreshCooldownOnNonNpcUnitKill != null && RefreshCooldownOnNonNpcUnitKill.Value; } private static bool HasNpcComponent(object unit) { if (unit == null || npcType == null) { return false; } Component val = (Component)((unit is Component) ? unit : null); if ((Object)(object)val == (Object)null) { return false; } Component component = val.GetComponent(npcType); return (Object)(object)component != (Object)null; } private static void ToggleOn(object equipmentManager, ToggleState state) { state.IsToggled = true; state.AttackInProgress = false; state.SheatheAfterAttack = false; state.SuppressMeleeUntilReleased = false; state.NextChargeAttemptTime = Time.time + ClampRetryInterval(); if (LogStateChanges.Value) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"Toggle melee ON."); } } InvokeChargeBasicMelee(equipmentManager); SetMeleePressed(equipmentManager, value: true); } private static void ToggleOff(object equipmentManager, ToggleState state) { if (LogStateChanges.Value) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"Toggle melee OFF."); } } state.IsToggled = false; state.NextChargeAttemptTime = 0f; state.SuppressMeleeUntilReleased = true; SetAlternativeMeleePressed(equipmentManager, value: false); SetMeleePressed(equipmentManager, value: false); object currentHoldable = GetCurrentHoldable(equipmentManager); if (state.AttackInProgress) { state.SheatheAfterAttack = true; if (LogStateChanges.Value) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)"Melee attack is in progress. Will sheathe after current attack finishes."); } } } else { state.AttackInProgress = false; state.SheatheAfterAttack = false; CleanCurrentMeleeWeaponState(currentHoldable); InvokeOnMeleeDone(equipmentManager); } } private static void MaintainToggledMelee(object equipmentManager, ToggleState state) { if (!state.AttackInProgress && !state.SheatheAfterAttack) { bool flag = IsAltFirePressed(equipmentManager); SetAlternativeMeleePressed(equipmentManager, flag); UpdateCurrentMeleeAlternativeAnimator(equipmentManager, flag); if (!IsInMeleeCharge(equipmentManager) && !(Time.time < state.NextChargeAttemptTime)) { state.NextChargeAttemptTime = Time.time + ClampRetryInterval(); InvokeChargeBasicMelee(equipmentManager); SetMeleePressed(equipmentManager, value: true); } } } private static bool IsKatanaDashContext(object walkerController, object equipmentManager, ToggleState toggleState, out object katana) { katana = null; if (walkerController == null || equipmentManager == null || toggleState == null) { return false; } if (!toggleState.IsToggled || toggleState.AttackInProgress || toggleState.SheatheAfterAttack) { return false; } object currentHoldable = GetCurrentHoldable(equipmentManager); if (!IsKatanaWeapon(currentHoldable)) { return false; } if (!IsInMeleeCharge(equipmentManager)) { return false; } katana = currentHoldable; return true; } private static bool IsKatanaDashHudContext(object walkerController, object equipmentManager, ToggleState toggleState, out object katana) { katana = null; if (walkerController == null || equipmentManager == null || toggleState == null) { return false; } if (!toggleState.IsToggled || toggleState.SheatheAfterAttack) { return false; } object currentHoldable = GetCurrentHoldable(equipmentManager); if (!IsKatanaWeapon(currentHoldable)) { return false; } katana = currentHoldable; return true; } private static void TryStartKatanaDash(object walkerController, object equipmentManager, object katana) { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) if (walkerController == null || equipmentManager == null || katana == null) { return; } KatanaDashState dashState = GetDashState(walkerController); EndPostDashHang(walkerController, dashState); if (dashState.IsDashing) { return; } if (Time.time < dashState.NextDashTime) { if (LogDash.Value) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("Katana Dash is cooling down: " + Mathf.Max(0f, dashState.NextDashTime - Time.time).ToString("F2") + "s")); } } return; } Vector3 lookingDirection = GetLookingDirection(walkerController); if (((Vector3)(ref lookingDirection)).sqrMagnitude < 0.0001f) { return; } ((Vector3)(ref lookingDirection)).Normalize(); Component val = (Component)((walkerController is Component) ? walkerController : null); if ((Object)(object)val == (Object)null) { return; } dashState.IsDashing = true; dashState.DashEndTime = Time.time + GetDashDuration(); dashState.NextDashTime = Time.time + GetDashCooldown(); dashState.Direction = lookingDirection; dashState.LastPosition = val.transform.position; dashState.KatanaWeapon = katana; dashState.HitUnitIds.Clear(); if (LogDash.Value) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)("Katana Dash started. direction=" + ((object)(Vector3)(ref lookingDirection)).ToString() + " duration=" + GetDashDuration().ToString("F2") + " distance=" + GetDashDistance().ToString("F2"))); } } } private static void UpdateKatanaDash(Component walkerComponent, KatanaDashState dashState) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)walkerComponent == (Object)null || dashState == null) { return; } Vector3 position = walkerComponent.transform.position; ProcessDashHits(walkerComponent, dashState, dashState.LastPosition, position); dashState.LastPosition = position; if (!(Time.time >= dashState.DashEndTime)) { return; } dashState.IsDashing = false; BeginPostDashHang(walkerComponent, dashState); if (LogDash.Value) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"Katana Dash ended. Post hang started."); } } } private static void BeginPostDashHang(Component walkerComponent, KatanaDashState dashState) { if ((Object)(object)walkerComponent == (Object)null || dashState == null) { return; } float dashPostHangDuration = GetDashPostHangDuration(); if (dashPostHangDuration <= 0f) { EndPostDashHang(walkerComponent, dashState); return; } dashState.PostHangActive = true; dashState.PostHangEndTime = Time.time + dashPostHangDuration; if (SuppressFallingAnimationDuringPostHang.Value) { SaveAndSetFallingEnabled(walkerComponent, dashState, value: false); } } private static void ApplyPostDashHangVelocity(Component walkerComponent, KatanaDashState dashState, ref Vector3 velocity) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)walkerComponent == (Object)null || dashState == null) { return; } if (Time.time >= dashState.PostHangEndTime) { EndPostDashHang(walkerComponent, dashState); return; } Vector3 up = walkerComponent.transform.up; float num = Vector3.Dot(velocity, up); float dashPostHangMaxDownwardSpeed = GetDashPostHangMaxDownwardSpeed(); if (num < 0f - dashPostHangMaxDownwardSpeed) { velocity += up * (0f - dashPostHangMaxDownwardSpeed - num); } } private static void EndPostDashHang(object walkerController, KatanaDashState dashState) { if (dashState != null) { Component val = (Component)((walkerController is Component) ? walkerController : null); if ((Object)(object)val != (Object)null) { RestoreFallingEnabled(val, dashState); } dashState.PostHangActive = false; dashState.PostHangEndTime = 0f; } } private static void SaveAndSetFallingEnabled(Component walkerComponent, KatanaDashState dashState, bool value) { if ((Object)(object)walkerComponent == (Object)null || dashState == null || pFallingEnabled == null) { return; } try { if (!dashState.FallingEnabledWasSaved) { object value2 = pFallingEnabled.GetValue(walkerComponent, null); if (value2 is bool) { dashState.PreviousFallingEnabled = (bool)value2; dashState.FallingEnabledWasSaved = true; } } pFallingEnabled.SetValue(walkerComponent, value, null); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogError((object)("Failed to set fallingEnabled during post dash hang: " + ex)); } } } private static void RestoreFallingEnabled(Component walkerComponent, KatanaDashState dashState) { if ((Object)(object)walkerComponent == (Object)null || dashState == null || pFallingEnabled == null || !dashState.FallingEnabledWasSaved) { return; } try { pFallingEnabled.SetValue(walkerComponent, dashState.PreviousFallingEnabled, null); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogError((object)("Failed to restore fallingEnabled after post dash hang: " + ex)); } } finally { dashState.FallingEnabledWasSaved = false; dashState.PreviousFallingEnabled = true; } } private static void ProcessDashHits(Component walkerComponent, KatanaDashState dashState, Vector3 start, Vector3 end) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)walkerComponent == (Object)null || dashState == null || dashState.KatanaWeapon == null) { return; } float dashHitRadius = GetDashHitRadius(); Vector3 val = end - start; Collider[] array = ((!(((Vector3)(ref val)).sqrMagnitude <= 0.0001f)) ? Physics.OverlapCapsule(start, end, dashHitRadius, -1, (QueryTriggerInteraction)2) : Physics.OverlapSphere(start, dashHitRadius, -1, (QueryTriggerInteraction)2)); if (array == null || array.Length == 0) { return; } object component = GetComponent(walkerComponent, unitType); foreach (Collider val2 in array) { if ((Object)(object)val2 == (Object)null) { continue; } object componentInParent = ((Component)val2).GetComponentInParent(unitType); if (componentInParent == null || componentInParent == component || IsPlayerUnit(componentInParent) || IsDeadUnit(componentInParent) || IsProtectedNpc(componentInParent)) { continue; } int instanceID = ((Object)componentInParent).GetInstanceID(); if (DashHitEachUnitOnce.Value && dashState.HitUnitIds.Contains(instanceID)) { continue; } Vector3 hitPoint = val2.ClosestPoint(end); if (!ApplyKatanaDashDamage(componentInParent, dashState.KatanaWeapon, hitPoint)) { continue; } dashState.HitUnitIds.Add(instanceID); if (LogDash.Value) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("Katana Dash hit unit: " + componentInParent)); } } } } private static bool ApplyKatanaDashDamage(object unit, object katana, Vector3 hitPoint) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (unit == null || katana == null) { return false; } try { float num = Convert.ToSingle(mWeaponGetDamage.Invoke(katana, null)); float num2 = num * GetDashDamageMultiplier(); object obj = mWeaponGetDamageType.Invoke(katana, null); object[] parameters = new object[5] { num2, obj, katana, hitmeshDataDefault, hitPoint }; object obj2 = mUnitReceiveDamageWithIDamager.Invoke(unit, parameters); return obj2 is bool && (bool)obj2; } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogError((object)("Failed to apply Katana Dash damage: " + ex)); } return false; } } private static Vector3 GetLookingDirection(object walkerController) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) Component val = (Component)((walkerController is Component) ? walkerController : null); if ((Object)(object)val == (Object)null) { return Vector3.zero; } object component = GetComponent(val, playerType); if (component != null && mPlayerDirectionLooking != null) { try { object obj = mPlayerDirectionLooking.Invoke(component, new object[1] { false }); if (obj is Vector3) { return (Vector3)obj; } } catch { } } if (component != null && fPlayerCamera != null) { try { object? value = fPlayerCamera.GetValue(component); Camera val2 = (Camera)((value is Camera) ? value : null); if ((Object)(object)val2 != (Object)null) { return ((Component)val2).transform.forward; } } catch { } } if ((Object)(object)Camera.main != (Object)null) { return ((Component)Camera.main).transform.forward; } return val.transform.forward; } private static bool IsKatanaWeapon(object weapon) { if (!IsMeleeWeapon(weapon)) { return false; } string value = KatanaNameKeywords.Value; if (string.IsNullOrEmpty(value)) { return false; } string[] array = value.Split(new char[1] { ',' }); string weaponSearchText = GetWeaponSearchText(weapon); for (int i = 0; i < array.Length; i++) { string value2 = array[i].Trim(); if (!string.IsNullOrEmpty(value2) && weaponSearchText.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } private static string GetWeaponSearchText(object weapon) { if (weapon == null) { return string.Empty; } string empty = string.Empty; empty += weapon.ToString(); empty += " "; Component val = (Component)((weapon is Component) ? weapon : null); if ((Object)(object)val != (Object)null) { empty += ((Object)val).name; empty += " "; empty += ((Object)val.gameObject).name; empty += " "; } if (pSourceName != null) { try { object value = pSourceName.GetValue(weapon, null); if (value != null) { empty += value.ToString(); empty += " "; } } catch { } } if (fWeaponDefinition != null) { try { object value2 = fWeaponDefinition.GetValue(weapon); if (value2 != null) { empty += value2.ToString(); empty += " "; } } catch { } } return empty; } private static object GetEquipmentManagerFromWalker(object walkerController) { if (walkerController == null || fWalkerEquipmentManager == null) { return null; } return fWalkerEquipmentManager.GetValue(walkerController); } private static object TryParseEntityAttribute(params string[] names) { if (entityAttributesType == null || names == null) { return null; } foreach (string value in names) { if (string.IsNullOrEmpty(value)) { continue; } try { if (Enum.IsDefined(entityAttributesType, value)) { return Enum.Parse(entityAttributesType, value); } } catch { } } return null; } private static InputAction GetSprintAction(object walkerController) { if (walkerController == null || pSprintAction == null) { return null; } try { object? value = pSprintAction.GetValue(walkerController, null); return (InputAction)((value is InputAction) ? value : null); } catch { return null; } } private static void InvokeToggleSprint(object walkerController, bool state) { if (walkerController == null || mToggleSprint == null) { return; } try { mToggleSprint.Invoke(walkerController, new object[1] { state }); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogError((object)("Failed to invoke ToggleSprint: " + ex)); } } } private static void CacheSafeMeleeAnimatorStateIfUseful(object weapon, string source) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (!IsMeleeWeapon(weapon)) { return; } Animator animator = GetAnimator(weapon); if ((Object)(object)animator == (Object)null) { return; } AnimatorStateInfo currentAnimatorStateInfo = animator.GetCurrentAnimatorStateInfo(0); string currentClipName = GetCurrentClipName(animator); if (!IsSafeMeleeDrawClip(currentClipName)) { return; } SafeAnimatorState value = SafeAnimatorStates.GetValue(weapon, (object _) => new SafeAnimatorState()); value.FullPathHash = ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).fullPathHash; value.ShortNameHash = ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).shortNameHash; value.ClipName = currentClipName; value.Valid = true; if (LogStateChanges.Value) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("Cached safe melee animator state from " + source + ": clip=" + currentClipName + " fullPathHash=" + ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).fullPathHash)); } } } private static bool IsSafeMeleeDrawClip(string clipName) { if (string.IsNullOrEmpty(clipName)) { return false; } if (ContainsIgnoreCase(clipName, "Slash")) { return false; } if (ContainsIgnoreCase(clipName, "Attack")) { return false; } if (ContainsIgnoreCase(clipName, "Fire")) { return false; } if (ContainsIgnoreCase(clipName, "ADS")) { return false; } if (ContainsIgnoreCase(clipName, "ToADS")) { return false; } if (ContainsIgnoreCase(clipName, "Charge") || ContainsIgnoreCase(clipName, "Charged")) { return false; } return ContainsIgnoreCase(clipName, "Equip"); } private static bool ContainsIgnoreCase(string value, string part) { return value != null && part != null && value.IndexOf(part, StringComparison.OrdinalIgnoreCase) >= 0; } private static void PrepareMeleeAnimatorForFutureDraw(object weapon) { if (!ResetMeleeAnimatorBeforeSheathe.Value || !IsMeleeWeapon(weapon)) { return; } try { InvokeSetAlternativeState(weapon, 0); if (fCurrentParries != null) { fCurrentParries.SetValue(weapon, 0); } Animator animator = GetAnimator(weapon); if ((Object)(object)animator == (Object)null) { return; } animator.SetBool("Charge", false); animator.SetBool("Sprinting", false); animator.SetBool("AlternativePressed", false); animator.ResetTrigger("Parry"); if (!SafeAnimatorStates.TryGetValue(weapon, out var value) || value == null || !value.Valid) { CacheSafeMeleeAnimatorStateIfUseful(weapon, "PrepareMeleeAnimatorForFutureDraw fallback"); if (!SafeAnimatorStates.TryGetValue(weapon, out value) || value == null || !value.Valid) { if (LogStateChanges.Value) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("No cached safe melee animator state available for " + weapon)); } } return; } } animator.Play(value.FullPathHash, 0, 0f); animator.Update(0f); if (LogStateChanges.Value) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)("Reset melee animator to safe draw state before sheathe: " + value.ClipName + " hash=" + value.FullPathHash)); } } } catch (Exception ex) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogError((object)("Failed to prepare melee animator for future draw: " + ex)); } } } private static void CleanCurrentMeleeWeaponState(object currentHoldable) { if (!IsMeleeWeapon(currentHoldable)) { return; } try { PrepareMeleeAnimatorForFutureDraw(currentHoldable); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogError((object)("Failed to clean melee weapon state: " + ex)); } } } private static string GetCurrentClipName(Animator animator) { if ((Object)(object)animator == (Object)null) { return string.Empty; } try { AnimatorClipInfo[] currentAnimatorClipInfo = animator.GetCurrentAnimatorClipInfo(0); if (currentAnimatorClipInfo != null && currentAnimatorClipInfo.Length != 0 && (Object)(object)((AnimatorClipInfo)(ref currentAnimatorClipInfo[0])).clip != (Object)null) { return ((Object)((AnimatorClipInfo)(ref currentAnimatorClipInfo[0])).clip).name; } } catch { return string.Empty; } return string.Empty; } private static float ClampRetryInterval() { if (ReChargeRetryInterval == null) { return 0.08f; } float value = ReChargeRetryInterval.Value; if (float.IsNaN(value) || float.IsInfinity(value)) { return 0.08f; } return Mathf.Clamp(value, 0.01f, 0.5f); } private static float GetDashCooldown() { return Mathf.Clamp(DashCooldown.Value, 0.1f, 60f); } private static float GetDashDistance() { return Mathf.Clamp(DashDistance.Value, 0.5f, 50f); } private static float GetDashDuration() { return Mathf.Clamp(DashDuration.Value, 0.03f, 2f); } private static float GetDashSpeed() { return GetDashDistance() / GetDashDuration(); } private static float GetDashHitRadius() { return Mathf.Clamp(DashHitRadius.Value, 0.1f, 5f); } private static float GetEntityStatValue(object stats, object attribute, bool preferStatus) { if (stats == null || attribute == null) { return 0f; } if (preferStatus && mEntityStatsGetStatus != null) { try { object value = mEntityStatsGetStatus.Invoke(stats, new object[1] { attribute }); return Convert.ToSingle(value); } catch { } } if (mEntityStatsGetAttribute != null) { try { object value2 = mEntityStatsGetAttribute.Invoke(stats, new object[1] { attribute }); return Convert.ToSingle(value2); } catch { } } if (!preferStatus && mEntityStatsGetStatus != null) { try { object value3 = mEntityStatsGetStatus.Invoke(stats, new object[1] { attribute }); return Convert.ToSingle(value3); } catch { } } return 0f; } private static float GetDashDamageMultiplier() { return Mathf.Clamp(DashDamageMultiplier.Value, 0f, 20f); } private static float GetHealAmountOnEnemyKill() { if (HealAmountOnEnemyKill == null) { return 5f; } float value = HealAmountOnEnemyKill.Value; if (float.IsNaN(value) || float.IsInfinity(value)) { return 5f; } return Mathf.Clamp(value, 0f, 999f); } private static float GetDashPostHangDuration() { if (DashPostHangDuration == null) { return 0.12f; } float value = DashPostHangDuration.Value; if (float.IsNaN(value) || float.IsInfinity(value)) { return 0.12f; } return Mathf.Clamp(value, 0f, 0.5f); } private static float GetDashPostHangMaxDownwardSpeed() { if (DashPostHangMaxDownwardSpeed == null) { return 0.5f; } float value = DashPostHangMaxDownwardSpeed.Value; if (float.IsNaN(value) || float.IsInfinity(value)) { return 0.5f; } return Mathf.Clamp(value, 0f, 20f); } private static float GetDashKillRefreshFeedbackDuration() { if (DashKillRefreshFeedbackDuration == null) { return 0.45f; } float value = DashKillRefreshFeedbackDuration.Value; if (float.IsNaN(value) || float.IsInfinity(value)) { return 0.45f; } return Mathf.Clamp(value, 0f, 3f); } private static float GetDashHealFeedbackDuration() { if (DashHealFeedbackDuration == null) { return 0.85f; } float value = DashHealFeedbackDuration.Value; if (float.IsNaN(value) || float.IsInfinity(value)) { return 0.85f; } return Mathf.Clamp(value, 0f, 3f); } private static bool WasMeleePressedThisFrame(object equipmentManager) { InputAction inputAction = GetInputAction(fMeleeFireAction, equipmentManager); InputAction inputAction2 = GetInputAction(fMeleeFireActionAlternative, equipmentManager); return WasPerformedThisFrame(inputAction) || WasPerformedThisFrame(inputAction2); } private static bool IsMeleeHeld(object equipmentManager) { InputAction inputAction = GetInputAction(fMeleeFireAction, equipmentManager); InputAction inputAction2 = GetInputAction(fMeleeFireActionAlternative, equipmentManager); bool flag = inputAction != null && inputAction.IsPressed(); bool flag2 = inputAction2 != null && inputAction2.IsPressed(); return flag || flag2; } private static bool IsAltFirePressed(object equipmentManager) { InputAction inputAction = GetInputAction(fAltFireAction, equipmentManager); return inputAction != null && inputAction.IsPressed(); } private static bool WasPerformedThisFrame(InputAction action) { return action != null && action.WasPerformedThisFrame(); } private static InputAction GetInputAction(FieldInfo field, object instance) { if (field == null || instance == null) { return null; } object? value = field.GetValue(instance); return (InputAction)((value is InputAction) ? value : null); } private static object GetCurrentHoldable(object equipmentManager) { if (fCurrentHoldable == null || equipmentManager == null) { return null; } return fCurrentHoldable.GetValue(equipmentManager); } private static object GetEquipmentManagerFromWeapon(object weapon) { if (fEquipmentManager == null || weapon == null) { return null; } return fEquipmentManager.GetValue(weapon); } private static bool IsMeleeWeapon(object holdable) { if (holdable == null) { return false; } if (!weaponType.IsInstanceOfType(holdable)) { return false; } object value = pIsMelee.GetValue(holdable, null); return value is bool && (bool)value; } private static bool IsMeleeCharging(object weapon) { if (weapon == null || mIsMeleeCharging == null) { return false; } object obj = mIsMeleeCharging.Invoke(weapon, null); return obj is bool && (bool)obj; } private static bool IsInMeleeCharge(object equipmentManager) { return GetBoolField(fIsInMeleeCharge, equipmentManager); } private static bool IsAimingInputHeld(object equipmentManager) { return GetBoolField(fAimingInputHeld, equipmentManager); } private static bool IsMeleeInputCoolingDown(object equipmentManager) { if (fMeleeInputCooldown == null || equipmentManager == null) { return false; } object value = fMeleeInputCooldown.GetValue(equipmentManager); if (!(value is float)) { return false; } return Time.time < (float)value; } private static bool GetBoolField(FieldInfo field, object instance) { if (field == null || instance == null) { return false; } object value = field.GetValue(instance); return value is bool && (bool)value; } private static void SetMeleePressed(object equipmentManager, bool value) { SetBoolField(fMeleePressed, equipmentManager, value); } private static void SetAlternativeMeleePressed(object equipmentManager, bool value) { SetBoolField(fAlternativeMeleePressed, equipmentManager, value); } private static void SetBoolField(FieldInfo field, object instance, bool value) { if (!(field == null) && instance != null) { field.SetValue(instance, value); } } private static void UpdateCurrentMeleeAlternativeAnimator(object equipmentManager, bool altHeld) { object currentHoldable = GetCurrentHoldable(equipmentManager); if (IsMeleeWeapon(currentHoldable)) { Animator animator = GetAnimator(currentHoldable); if (!((Object)(object)animator == (Object)null)) { animator.SetBool("AlternativePressed", altHeld); } } } private static Animator GetAnimator(object holdable) { if (holdable == null || pAnimator == null) { return null; } object? value = pAnimator.GetValue(holdable, null); return (Animator)((value is Animator) ? value : null); } private static void InvokeChargeBasicMelee(object equipmentManager) { try { mChargeBasicMelee.Invoke(equipmentManager, null); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogError((object)("Failed to invoke ChargeBasicMelee: " + ex)); } } } private static void InvokeUseBasicMelee(object equipmentManager) { try { mUseBasicMelee.Invoke(equipmentManager, null); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogError((object)("Failed to invoke UseBasicMelee: " + ex)); } } } private static void InvokeOnMeleeDone(object equipmentManager) { try { mOnMeleeDone.Invoke(equipmentManager, null); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogError((object)("Failed to invoke OnMeleeDone: " + ex)); } } } private static void InvokeSetAlternativeState(object weapon, int value) { if (weapon == null || mSetAlternativeState == null) { return; } try { mSetAlternativeState.Invoke(weapon, new object[1] { value }); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogError((object)("Failed to invoke SetAlternativeState: " + ex)); } } } private static object GetComponent(Component component, Type type) { if ((Object)(object)component == (Object)null || type == null) { return null; } return component.GetComponent(type); } private static bool IsPlayerUnit(object unit) { if (unit == null) { return false; } try { object obj = null; if (pUnitIsPlayer != null) { obj = pUnitIsPlayer.GetValue(unit, null); } else if (fUnitIsPlayer != null) { obj = fUnitIsPlayer.GetValue(unit); } return obj is bool && (bool)obj; } catch { return false; } } private static bool IsDeadUnit(object unit) { if (unit == null) { return true; } try { object obj = null; if (pUnitState != null) { obj = pUnitState.GetValue(unit, null); } else if (fUnitState != null) { obj = fUnitState.GetValue(unit); } if (obj == null) { return false; } return string.Equals(obj.ToString(), "Dead", StringComparison.OrdinalIgnoreCase); } catch { return false; } } private static bool IsProtectedNpc(object unit) { if (unit == null || npcType == null || pNpcIsProtectedNpc == null) { return false; } Component val = (Component)((unit is Component) ? unit : null); if ((Object)(object)val == (Object)null) { return false; } Component component = val.GetComponent(npcType); if ((Object)(object)component == (Object)null) { return false; } try { object value = pNpcIsProtectedNpc.GetValue(component, null); return value is bool && (bool)value; } catch { return false; } } private static void DrawKatanaDashHud(KatanaDashState dashState, bool canUseDashNow) { //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_011d: 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_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_0402: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_031a: Unknown result type (might be due to invalid IL or missing references) //IL_034b: Unknown result type (might be due to invalid IL or missing references) //IL_0361: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_03c0: Unknown result type (might be due to invalid IL or missing references) //IL_0447: Unknown result type (might be due to invalid IL or missing references) //IL_042c: Unknown result type (might be due to invalid IL or missing references) //IL_03f2: Unknown result type (might be due to invalid IL or missing references) //IL_03d7: Unknown result type (might be due to invalid IL or missing references) //IL_047c: Unknown result type (might be due to invalid IL or missing references) //IL_049c: Unknown result type (might be due to invalid IL or missing references) //IL_0520: Unknown result type (might be due to invalid IL or missing references) //IL_04d2: Unknown result type (might be due to invalid IL or missing references) //IL_050c: Unknown result type (might be due to invalid IL or missing references) //IL_0555: Unknown result type (might be due to invalid IL or missing references) //IL_053a: Unknown result type (might be due to invalid IL or missing references) //IL_0560: Unknown result type (might be due to invalid IL or missing references) //IL_059a: Unknown result type (might be due to invalid IL or missing references) //IL_057f: Unknown result type (might be due to invalid IL or missing references) //IL_05a5: Unknown result type (might be due to invalid IL or missing references) //IL_05cd: Unknown result type (might be due to invalid IL or missing references) //IL_05c6: Unknown result type (might be due to invalid IL or missing references) //IL_05d9: Unknown result type (might be due to invalid IL or missing references) EnsureDashHudTextures(); float num = Mathf.Clamp(DashHudIconSize.Value, 48f, 128f); float num2 = num + 26f; float num3 = num + 42f; float num4 = (float)Screen.width - num2 - 34f; float num5 = (float)Screen.height - num3 - 34f; Rect rect = default(Rect); ((Rect)(ref rect))..ctor(num4, num5, num2, num3); Rect val = default(Rect); ((Rect)(ref val))..ctor(num4 + 13f, num5 + 10f, num, num); Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(num4 + 13f, num5 + 13f + num, num, 22f); float num6 = Mathf.Max(0f, dashState.NextDashTime - Time.time); float num7 = Mathf.Max(0.01f, GetDashCooldown()); bool flag = num6 <= 0f && !dashState.IsDashing && canUseDashNow; float num8 = Mathf.Clamp01(num6 / num7); bool flag2 = Time.time < dashState.KillRefreshFeedbackEndTime; bool flag3 = Time.time < dashState.HealFeedbackEndTime; Color color = GUI.color; Color val2 = (flag2 ? new Color(0.45f, 1f, 0.55f, 1f) : new Color(0.95f, 0.95f, 0.9f, 0.85f)); DrawRect(new Rect(((Rect)(ref rect)).x + 4f, ((Rect)(ref rect)).y + 4f, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height), new Color(0f, 0f, 0f, 0.35f)); DrawRect(rect, new Color(0.02f, 0.02f, 0.025f, 0.78f)); DrawBorder(rect, flag2 ? 3f : 2f, (Color)(flag ? val2 : new Color(0.45f, 0.45f, 0.45f, 0.85f))); DrawBorder(new Rect(((Rect)(ref val)).x - 3f, ((Rect)(ref val)).y - 3f, ((Rect)(ref val)).width + 6f, ((Rect)(ref val)).height + 6f), flag2 ? 3f : 2f, flag2 ? new Color(0.3f, 1f, 0.45f, 1f) : new Color(0f, 0f, 0f, 0.9f)); if ((Object)(object)DashIconTexture != (Object)null) { GUI.color = (Color)((flag || flag2) ? Color.white : new Color(0.55f, 0.55f, 0.55f, 1f)); GUI.DrawTexture(val, (Texture)(object)DashIconTexture, (ScaleMode)2, true); } else { DrawRect(val, new Color(0.15f, 0.15f, 0.16f, 1f)); DrawOutlinedLabel(val, "DASH", 18, Color.white, (TextAnchor)4); } if (!flag) { DrawRect(val, new Color(0f, 0f, 0f, 0.45f)); Rect rect3 = default(Rect); ((Rect)(ref rect3))..ctor(((Rect)(ref val)).x, ((Rect)(ref val)).y, ((Rect)(ref val)).width, ((Rect)(ref val)).height * num8); DrawRect(rect3, new Color(0f, 0f, 0f, 0.55f)); DrawOutlinedLabel(text: (!(num6 > 0f)) ? "—" : ((!(num6 >= 1f)) ? num6.ToString("F1") : Mathf.CeilToInt(num6).ToString()), rect: val, fontSize: (num6 >= 1f) ? 34 : 28, color: (Color)(flag2 ? new Color(0.7f, 1f, 0.75f, 1f) : Color.white), alignment: (TextAnchor)4); } else { DrawBorder(val, flag2 ? 3f : 2f, flag2 ? new Color(0.35f, 1f, 0.45f, 1f) : new Color(0.75f, 1f, 0.85f, 0.95f)); } if (flag2) { DrawOutlinedLabel(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y - 22f, ((Rect)(ref val)).width, 20f), "RESET", 14, new Color(0.45f, 1f, 0.55f, 1f), (TextAnchor)4); } if (flag3) { DrawOutlinedLabel(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y - 42f, ((Rect)(ref val)).width, 20f), "+" + dashState.HealFeedbackAmount.ToString("F0") + " HP", 15, new Color(0.45f, 1f, 0.55f, 1f), (TextAnchor)4); } string dashKeyLabel = GetDashKeyLabel(); DrawRect(rect2, flag ? new Color(0.1f, 0.1f, 0.1f, 0.9f) : new Color(0.05f, 0.05f, 0.05f, 0.9f)); DrawBorder(rect2, 1f, flag2 ? new Color(0.45f, 1f, 0.55f, 0.9f) : new Color(0.7f, 0.7f, 0.7f, 0.75f)); DrawOutlinedLabel(rect2, dashKeyLabel, 14, (Color)((flag || flag2) ? Color.white : new Color(0.65f, 0.65f, 0.65f, 1f)), (TextAnchor)4); GUI.color = color; } private static void EnsureDashHudTextures() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown if ((Object)(object)HudPixelTexture == (Object)null) { HudPixelTexture = new Texture2D(1, 1, (TextureFormat)4, false); ((Object)HudPixelTexture).name = "MeleeExpansion_HudPixel"; HudPixelTexture.SetPixel(0, 0, Color.white); HudPixelTexture.Apply(false, true); } if (DashIconLoadAttempted) { return; } DashIconLoadAttempted = true; try { string text = ((DashIconFileName != null) ? DashIconFileName.Value : "dash_icon.png"); if (string.IsNullOrEmpty(text)) { return; } string text2 = Path.Combine(PluginDirectory, text); if (!File.Exists(text2)) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("Dash icon PNG not found: " + text2)); } return; } byte[] bytes = File.ReadAllBytes(text2); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); ((Object)val).name = "MeleeExpansion_DashIcon"; if (!LoadImageReflective(val, bytes, markNonReadable: false)) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)("Failed to load dash icon PNG: " + text2)); } Object.Destroy((Object)(object)val); return; } ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; DashIconTexture = val; if (LogDash != null && LogDash.Value) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogInfo((object)("Loaded dash icon PNG: " + text2)); } } } catch (Exception ex) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogError((object)("Failed to load dash icon PNG: " + ex)); } } } private static bool LoadImageReflective(Texture2D texture, byte[] bytes, bool markNonReadable) { if ((Object)(object)texture == (Object)null || bytes == null || bytes.Length == 0) { return false; } try { Type type = AccessTools.TypeByName("UnityEngine.ImageConversion"); if (type == null) { ManualLogSource log = Log; if (log != null) { log.LogError((object)"Could not find UnityEngine.ImageConversion type."); } return false; } MethodInfo methodInfo = AccessTools.Method(type, "LoadImage", new Type[3] { typeof(Texture2D), typeof(byte[]), typeof(bool) }, (Type[])null); if (methodInfo == null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogError((object)"Could not find ImageConversion.LoadImage(Texture2D, byte[], bool)."); } return false; } object obj = methodInfo.Invoke(null, new object[3] { texture, bytes, markNonReadable }); return obj is bool && (bool)obj; } catch (Exception ex) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogError((object)("Failed to load image through reflection: " + ex)); } return false; } } private static void DrawRect(Rect rect, Color color) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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_002b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)HudPixelTexture == (Object)null)) { Color color2 = GUI.color; GUI.color = color; GUI.DrawTexture(rect, (Texture)(object)HudPixelTexture); GUI.color = color2; } } private static void DrawBorder(Rect rect, float thickness, Color color) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) DrawRect(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, thickness), color); DrawRect(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax - thickness, ((Rect)(ref rect)).width, thickness), color); DrawRect(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, thickness, ((Rect)(ref rect)).height), color); DrawRect(new Rect(((Rect)(ref rect)).xMax - thickness, ((Rect)(ref rect)).y, thickness, ((Rect)(ref rect)).height), color); } private static void DrawOutlinedLabel(Rect rect, string text, int fontSize, Color color, TextAnchor alignment) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: 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) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) GUIStyle val = new GUIStyle(GUI.skin.label); val.alignment = alignment; val.fontSize = fontSize; val.fontStyle = (FontStyle)1; val.normal.textColor = color; GUIStyle val2 = new GUIStyle(val); val2.normal.textColor = Color.black; Rect val3 = rect; ((Rect)(ref val3)).x = ((Rect)(ref val3)).x - 1f; GUI.Label(val3, text, val2); val3 = rect; ((Rect)(ref val3)).x = ((Rect)(ref val3)).x + 1f; GUI.Label(val3, text, val2); val3 = rect; ((Rect)(ref val3)).y = ((Rect)(ref val3)).y - 1f; GUI.Label(val3, text, val2); val3 = rect; ((Rect)(ref val3)).y = ((Rect)(ref val3)).y + 1f; GUI.Label(val3, text, val2); GUI.Label(rect, text, val); } private static string GetDashKeyLabel() { if (DashHudUseActualSprintBinding != null && DashHudUseActualSprintBinding.Value && lastKnownWalkerController != null) { try { InputAction sprintAction = GetSprintAction(lastKnownWalkerController); if (sprintAction != null) { string bindingDisplayString = InputActionRebindingExtensions.GetBindingDisplayString(sprintAction, (DisplayStringOptions)0, (string)null); if (!string.IsNullOrEmpty(bindingDisplayString)) { return bindingDisplayString.ToUpperInvariant(); } } } catch { } } if (DashHudFallbackKeyLabel != null && !string.IsNullOrEmpty(DashHudFallbackKeyLabel.Value)) { return DashHudFallbackKeyLabel.Value.ToUpperInvariant(); } return "SPRINT"; } }