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<int> HitUnitIds = new HashSet<int>();
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<bool> EnableMod;
internal static ConfigEntry<bool> FirePerformsMeleeAttack;
internal static ConfigEntry<bool> ResetMeleeAnimatorBeforeSheathe;
internal static ConfigEntry<float> ReChargeRetryInterval;
internal static ConfigEntry<bool> LogStateChanges;
internal static ConfigEntry<bool> EnableKatanaDash;
internal static ConfigEntry<string> KatanaNameKeywords;
internal static ConfigEntry<float> DashCooldown;
internal static ConfigEntry<float> DashDistance;
internal static ConfigEntry<float> DashDuration;
internal static ConfigEntry<float> DashHitRadius;
internal static ConfigEntry<float> DashDamageMultiplier;
internal static ConfigEntry<bool> DashHitEachUnitOnce;
internal static ConfigEntry<bool> RefreshCooldownOnPlayerKill;
internal static ConfigEntry<bool> RefreshRequiresKatanaStance;
internal static ConfigEntry<bool> RefreshCooldownOnNonNpcUnitKill;
internal static ConfigEntry<bool> HealOnEnemyKill;
internal static ConfigEntry<float> HealAmountOnEnemyKill;
internal static ConfigEntry<float> DashPostHangDuration;
internal static ConfigEntry<float> DashPostHangMaxDownwardSpeed;
internal static ConfigEntry<bool> SuppressFallingAnimationDuringPostHang;
internal static ConfigEntry<bool> EnableDashHud;
internal static ConfigEntry<string> DashIconFileName;
internal static ConfigEntry<float> DashHudIconSize;
internal static ConfigEntry<bool> DashHudUseActualSprintBinding;
internal static ConfigEntry<string> DashHudFallbackKeyLabel;
internal static ConfigEntry<float> DashKillRefreshFeedbackDuration;
internal static ConfigEntry<float> DashHealFeedbackDuration;
internal static ConfigEntry<bool> 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<object, ToggleState> ToggleStates = new ConditionalWeakTable<object, ToggleState>();
private static readonly ConditionalWeakTable<object, SafeAnimatorState> SafeAnimatorStates = new ConditionalWeakTable<object, SafeAnimatorState>();
private static readonly ConditionalWeakTable<object, KatanaDashState> DashStates = new ConditionalWeakTable<object, KatanaDashState>();
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<bool>("General", "EnableMod", true, "Enable Melee Expansion.");
FirePerformsMeleeAttack = ((BaseUnityPlugin)this).Config.Bind<bool>("ToggleMelee", "FirePerformsMeleeAttack", true, "When melee stance is toggled on, pressing the normal Fire action performs one melee attack.");
ReChargeRetryInterval = ((BaseUnityPlugin)this).Config.Bind<float>("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<float>(0.01f, 0.5f), Array.Empty<object>()));
ResetMeleeAnimatorBeforeSheathe = ((BaseUnityPlugin)this).Config.Bind<bool>("ToggleMelee", "ResetMeleeAnimatorBeforeSheathe", true, "Before sheathing a toggled melee weapon, reset its Animator to the cached safe equip state.");
EnableKatanaDash = ((BaseUnityPlugin)this).Config.Bind<bool>("KatanaDash", "EnableKatanaDash", true, "Enable Katana Dash Strike.");
KatanaNameKeywords = ((BaseUnityPlugin)this).Config.Bind<string>("KatanaDash", "KatanaNameKeywords", "Katana,Wakizashi,BiggerKatana", "Comma-separated keywords used to detect katana-like melee weapons.");
DashCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("KatanaDash", "Cooldown", 5f, new ConfigDescription("Dash cooldown in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 60f), Array.Empty<object>()));
DashDistance = ((BaseUnityPlugin)this).Config.Bind<float>("KatanaDash", "Distance", 8f, new ConfigDescription("Dash distance.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 50f), Array.Empty<object>()));
DashDuration = ((BaseUnityPlugin)this).Config.Bind<float>("KatanaDash", "Duration", 0.22f, new ConfigDescription("Dash duration in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.03f, 2f), Array.Empty<object>()));
DashHitRadius = ((BaseUnityPlugin)this).Config.Bind<float>("KatanaDash", "HitRadius", 1f, new ConfigDescription("Radius used to scan units along the dash path.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
DashDamageMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("KatanaDash", "DamageMultiplier", 1f, new ConfigDescription("Dash damage multiplier applied to the current katana weapon damage.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
DashHitEachUnitOnce = ((BaseUnityPlugin)this).Config.Bind<bool>("KatanaDash", "HitEachUnitOnce", true, "If true, each unit can only be damaged once per dash.");
RefreshCooldownOnPlayerKill = ((BaseUnityPlugin)this).Config.Bind<bool>("KatanaDash", "RefreshCooldownOnPlayerKill", true, "Refresh Katana Dash cooldown when the player kills an enemy.");
RefreshRequiresKatanaStance = ((BaseUnityPlugin)this).Config.Bind<bool>("KatanaDash", "RefreshRequiresKatanaStance", true, "If true, kill refresh only works while the player is in toggled katana stance.");
RefreshCooldownOnNonNpcUnitKill = ((BaseUnityPlugin)this).Config.Bind<bool>("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<bool>("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<float>("KatanaDash", "HealAmountOnEnemyKill", 5f, new ConfigDescription("Health restored when killing an enemy NPC.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 999f), Array.Empty<object>()));
DashPostHangDuration = ((BaseUnityPlugin)this).Config.Bind<float>("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<float>(0f, 0.5f), Array.Empty<object>()));
DashPostHangMaxDownwardSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("KatanaDash", "PostHangMaxDownwardSpeed", 0.5f, new ConfigDescription("Maximum downward speed during post-dash hang. 0 means no downward velocity during hang.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
SuppressFallingAnimationDuringPostHang = ((BaseUnityPlugin)this).Config.Bind<bool>("KatanaDash", "SuppressFallingAnimationDuringPostHang", true, "Temporarily suppress the Falling animation during post-dash hang.");
EnableDashHud = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "EnableDashHud", true, "Show a bottom-right Katana Dash cooldown HUD.");
DashIconFileName = ((BaseUnityPlugin)this).Config.Bind<string>("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<float>("UI", "DashHudIconSize", 76f, new ConfigDescription("Dash HUD icon size in pixels.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(48f, 128f), Array.Empty<object>()));
DashHudUseActualSprintBinding = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "DashHudUseActualSprintBinding", true, "Show the actual Sprint binding from the game's InputAction when possible.");
DashHudFallbackKeyLabel = ((BaseUnityPlugin)this).Config.Bind<string>("UI", "DashHudFallbackKeyLabel", "SHIFT", "Fallback key label shown on the Dash HUD.");
DashKillRefreshFeedbackDuration = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "DashKillRefreshFeedbackDuration", 0.45f, new ConfigDescription("Seconds to flash the Dash HUD when a kill refreshes the cooldown.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
DashHealFeedbackDuration = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "DashHealFeedbackDuration", 0.85f, new ConfigDescription("Seconds to show +HP feedback on enemy kill.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
LogStateChanges = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogStateChanges", false, "Log toggle melee state changes.");
LogDash = ((BaseUnityPlugin)this).Config.Bind<bool>("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, "<isInMeleeCharge>k__BackingField", "isInMeleeCharge");
fMeleePressed = FindFieldRecursive(equipmentManagerType, "<meleePressed>k__BackingField", "meleePressed");
fAlternativeMeleePressed = FindFieldRecursive(equipmentManagerType, "<alternativeMeleePressed>k__BackingField", "alternativeMeleePressed");
fAimingInputHeld = FindFieldRecursive(equipmentManagerType, "<AimingInputHeld>k__BackingField", "AimingInputHeld");
fMeleeInputCooldown = FindFieldRecursive(equipmentManagerType, "meleeInputCooldown");
fCurrentParries = FindFieldRecursive(weaponType, "currentParries");
fWeaponDefinition = FindFieldRecursive(weaponType, "weaponDefinition", "<weaponDefinition>k__BackingField");
fUnitState = FindFieldRecursive(unitType, "UnitState", "<UnitState>k__BackingField");
fUnitIsPlayer = FindFieldRecursive(unitType, "isPlayer", "<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";
}
}