using HarmonyLib; using KFCommonUtilityLib; using KFCommonUtilityLib.Scripts.Attributes; using KFCommonUtilityLib.Scripts.StaticManagers; using System.Collections.Generic; using System.Reflection.Emit; using UniLinq; using UnityEngine; using static ItemModuleMultiItem; [TypeTarget(typeof(ItemClass)), TypeDataTarget(typeof(MultiItemInvData))] public class ItemModuleMultiItem { private string boundItemName; private ItemClass boundItemClass; private ItemClass itemClass; public ItemClass BoundItemClass { get { if (boundItemClass == null && !string.IsNullOrEmpty(boundItemName)) { boundItemClass = ItemClass.GetItemClass(boundItemName, true); } return boundItemClass; } } [HarmonyPatch(nameof(ItemClass.Init)), MethodTargetPostfix] public void Postfix_Init(ItemClass __instance) { itemClass = __instance; __instance.Properties.ParseString("BoundItemName", ref boundItemName); } [HarmonyPatch(nameof(ItemClass.StartHolding)), MethodTargetPrefix] public bool Prefix_StartHolding(ItemInventoryData _data, MultiItemInvData __customData) { var boundInvData = __customData.boundInvData; ItemValue boundItemValue = boundInvData.itemStack.itemValue; boundItemValue.Quality = _data.itemValue.Quality; boundItemValue.Metadata = _data.itemValue.Metadata; boundItemValue.Modifications = _data.itemValue.Modifications; boundItemValue.CosmeticMods = _data.itemValue.CosmeticMods; return true; } [HarmonyPatch(nameof(ItemClass.CleanupHoldingActions)), MethodTargetPostfix] public void Postfix_CleanupHoldingActions(MultiItemInvData __customData) { BoundItemClass?.CleanupHoldingActions(__customData.boundInvData); } [HarmonyPatch(nameof(ItemClass.ConsumeScrollWheel)), MethodTargetPrefix] public bool Prefix_ConsumeScrollWheel(MultiItemInvData __customData, float _scrollWheelInput, PlayerActionsLocal _playerInput, ref bool __result) { if (__customData.useBound && BoundItemClass != null) { SetBoundParams(__customData.originalData.holdingEntity.MinEventContext, __customData); __result = boundItemClass.ConsumeScrollWheel(__customData.boundInvData, _scrollWheelInput, _playerInput); RestoreParams(__customData.originalData.holdingEntity.MinEventContext, __customData); return false; } return true; } [HarmonyPatch(nameof(ItemClass.ExecuteAction)), MethodTargetPrefix] public bool Prefix_ExecuteAction(bool _bReleased, MultiItemInvData __customData) { if (!_bReleased) { bool flag = IsBoundActionRunning(__customData); return !flag; } return true; } public bool IsBoundActionRunning(MultiItemInvData __customData) { bool useBound = __customData.useBound; __customData.useBound = true; SetBoundParams(__customData.originalData.holdingEntity.MinEventContext, __customData); bool flag = __customData.boundInvData.item.IsActionRunning(__customData.boundInvData); RestoreParams(__customData.originalData.holdingEntity.MinEventContext, __customData); __customData.useBound = useBound; return flag; } [HarmonyPatch(nameof(ItemClass.GetCameraShakeType)), MethodTargetPrefix] public bool Prefix_GetCameraShakeType(MultiItemInvData __customData, ref EnumCameraShake __result) { if (__customData.useBound && BoundItemClass != null) { SetBoundParams(__customData.originalData.holdingEntity.MinEventContext, __customData); __result = boundItemClass.GetCameraShakeType(__customData.boundInvData); RestoreParams(__customData.originalData.holdingEntity.MinEventContext, __customData); return false; } return true; } [HarmonyPatch(nameof(ItemClass.GetFocusType)), MethodTargetPrefix] public bool Prefix_GetFocusType(MultiItemInvData __customData, ref RenderCubeType __result) { if (__customData.useBound && BoundItemClass != null) { SetBoundParams(__customData.originalData.holdingEntity.MinEventContext, __customData); __result = boundItemClass.GetFocusType(__customData.boundInvData); RestoreParams(__customData.originalData.holdingEntity.MinEventContext, __customData); return false; } return true; } [HarmonyPatch(nameof(ItemClass.GetIronSights)), MethodTargetPrefix] public bool Prefix_GetIronSights(MultiItemInvData __customData, ref float _fov) { if (__customData.useBound && BoundItemClass != null) { SetBoundParams(__customData.originalData.holdingEntity.MinEventContext, __customData); boundItemClass.GetIronSights(__customData.boundInvData, out _fov); RestoreParams(__customData.originalData.holdingEntity.MinEventContext, __customData); return false; } return true; } [HarmonyPatch(nameof(ItemClass.IsActionRunning)), MethodTargetPostfix] public void Postfix_IsActionRunning(MultiItemInvData __customData, ref bool __result) { if (!__result && BoundItemClass != null) { bool useBound = __customData.useBound; __customData.useBound = true; SetBoundParams(__customData.originalData.holdingEntity.MinEventContext, __customData); __result |= boundItemClass.IsActionRunning(__customData.boundInvData); RestoreParams(__customData.originalData.holdingEntity.MinEventContext, __customData); __customData.useBound = useBound; } } [HarmonyPatch(nameof(ItemClass.IsHUDDisabled)), MethodTargetPostfix] public void Postfix_IsHUDDisabled(MultiItemInvData __customData, ref bool __result) { if (!__result && BoundItemClass != null) { bool useBound = __customData.useBound; __customData.useBound = true; SetBoundParams(__customData.originalData.holdingEntity.MinEventContext, __customData); __result |= boundItemClass.IsHUDDisabled(__customData.boundInvData); RestoreParams(__customData.originalData.holdingEntity.MinEventContext, __customData); __customData.useBound = useBound; } } [HarmonyPatch(nameof(ItemClass.OnHoldingItemActivated)), MethodTargetPostfix] public void Postfix_OnHoldingItemActivated(MultiItemInvData __customData) { if (BoundItemClass != null) { bool useBound = __customData.useBound; __customData.useBound = true; SetBoundParams(__customData.originalData.holdingEntity.MinEventContext, __customData); boundItemClass.OnHoldingItemActivated(__customData.boundInvData); RestoreParams(__customData.originalData.holdingEntity.MinEventContext, __customData); __customData.useBound = useBound; } } [HarmonyPatch(nameof(ItemClass.OnHoldingReset)), MethodTargetPostfix] public void Postfix_OnHoldingReset(MultiItemInvData __customData) { if (BoundItemClass != null) { bool useBound = __customData.useBound; __customData.useBound = true; SetBoundParams(__customData.originalData.holdingEntity.MinEventContext, __customData); boundItemClass.OnHoldingReset(__customData.boundInvData); RestoreParams(__customData.originalData.holdingEntity.MinEventContext, __customData); __customData.useBound = useBound; } } [HarmonyPatch(nameof(ItemClass.OnHoldingUpdate)), MethodTargetPostfix] public void Postfix_OnHoldingUpdate(MultiItemInvData __customData) { if (BoundItemClass != null) { bool useBound = __customData.useBound; __customData.useBound = true; SetBoundParams(__customData.originalData.holdingEntity.MinEventContext, __customData); boundItemClass.OnHoldingUpdate(__customData.boundInvData); RestoreParams(__customData.originalData.holdingEntity.MinEventContext, __customData); __customData.useBound = useBound; } } [HarmonyPatch(nameof(ItemClass.OnHUD)), MethodTargetPrefix] public bool Prefix_OnHUD(MultiItemInvData __customData, int _x, int _y) { if (__customData.useBound && BoundItemClass != null) { SetBoundParams(__customData.originalData.holdingEntity.MinEventContext, __customData); boundItemClass.OnHUD(__customData.boundInvData, _x, _y); RestoreParams(__customData.originalData.holdingEntity.MinEventContext, __customData); return false; } return true; } [HarmonyPatch(nameof(ItemClass.OnScreenOverlay)), MethodTargetPrefix] public bool Prefix_OnScreenOverlay(MultiItemInvData __customData) { if (__customData.useBound && BoundItemClass != null) { SetBoundParams(__customData.originalData.holdingEntity.MinEventContext, __customData); boundItemClass.OnScreenOverlay(__customData.boundInvData); RestoreParams(__customData.originalData.holdingEntity.MinEventContext, __customData); return false; } return true; } [HarmonyPatch(nameof(ItemClass.StartHolding)), MethodTargetPostfix] public void Postfix_StartHolding(MultiItemInvData __customData, Transform _modelTransform) { if (BoundItemClass != null) { bool useBound = __customData.useBound; __customData.useBound = true; SetBoundParams(__customData.originalData.holdingEntity.MinEventContext, __customData); boundItemClass.StartHolding(__customData.boundInvData, _modelTransform); RestoreParams(__customData.originalData.holdingEntity.MinEventContext, __customData); __customData.useBound = useBound; } } [HarmonyPatch(nameof(ItemClass.StopHolding)), MethodTargetPostfix] public void Postfix_StopHolding(MultiItemInvData __customData, Transform _modelTransform) { if (BoundItemClass != null) { bool useBound = __customData.useBound; __customData.useBound = true; SetBoundParams(__customData.originalData.holdingEntity.MinEventContext, __customData); boundItemClass.StopHolding(__customData.boundInvData, _modelTransform); RestoreParams(__customData.originalData.holdingEntity.MinEventContext, __customData); MultiActionManager.SetMappingForEntity(__customData.originalData.holdingEntity.entityId, null); __customData.useBound = useBound; } } public void SetBoundParams(MinEventParams param, MultiItemInvData data) { param.ItemInventoryData = data.boundInvData; param.ItemValue = data.boundInvData.itemStack.itemValue; if (data.originalData.actionData[0] is IModuleContainerFor dataModule) { MultiActionManager.SetMappingForEntity(data.originalData.holdingEntity.entityId, null); } } public void RestoreParams(MinEventParams param, MultiItemInvData data) { param.ItemInventoryData = data.originalData; param.ItemValue = data.originalData.itemStack.itemValue; if (data.originalData.actionData[0] is IModuleContainerFor dataModule) { MultiActionManager.SetMappingForEntity(data.originalData.holdingEntity.entityId, dataModule.Instance.mapping); } } public class MultiItemInvData { public ItemInventoryData originalData; public ItemInventoryData boundInvData; public ItemModuleMultiItem itemModule; public bool useBound; public MultiItemInvData(ItemInventoryData _invData, ItemClass _item, ItemStack _itemStack, IGameManager _gameManager, EntityAlive _holdingEntity, int _slotIdx, ItemModuleMultiItem module) { itemModule = module; originalData = _invData; if (module.BoundItemClass != null) { boundInvData = module.BoundItemClass.CreateInventoryData(new ItemStack(new ItemValue(module.BoundItemClass.Id), 1), _gameManager, _holdingEntity, _slotIdx); } } } } [HarmonyPatch] public static class MultiItemPatches { [HarmonyPatch(typeof(Inventory), nameof(Inventory.holdingCount), MethodType.Getter)] [HarmonyPrefix] public static bool Prefix_holdingCount_Inventory(ItemInventoryData[] ___slots, int ___m_HoldingItemIdx, ref int __result) { if (___slots[___m_HoldingItemIdx] is IModuleContainerFor multiInvData && multiInvData.Instance.useBound) { __result = multiInvData.Instance.boundInvData.itemStack.count; return false; } return true; } [HarmonyPatch(typeof(Inventory), nameof(Inventory.holdingItem), MethodType.Getter)] [HarmonyPrefix] public static bool Prefix_holdingItem_Inventory(ItemInventoryData[] ___slots, int ___m_HoldingItemIdx, ref ItemClass __result) { if (___slots[___m_HoldingItemIdx] is IModuleContainerFor multiInvData && multiInvData.Instance.useBound) { __result = multiInvData.Instance.boundInvData.item; return false; } return true; } [HarmonyPatch(typeof(Inventory), nameof(Inventory.holdingItemData), MethodType.Getter)] [HarmonyPrefix] public static bool Prefix_holdingItemData_Inventory(ItemInventoryData[] ___slots, int ___m_HoldingItemIdx, ref ItemInventoryData __result) { if (___slots[___m_HoldingItemIdx] is IModuleContainerFor multiInvData && multiInvData.Instance.useBound) { __result = multiInvData.Instance.boundInvData; return false; } return true; } [HarmonyPatch(typeof(Inventory), nameof(Inventory.holdingItemItemValue), MethodType.Getter)] [HarmonyPrefix] public static bool Prefix_holdingItemItemValue_Inventory(ItemInventoryData[] ___slots, int ___m_HoldingItemIdx, ref ItemValue __result) { if (___slots[___m_HoldingItemIdx] is IModuleContainerFor multiInvData && multiInvData.Instance.useBound) { __result = multiInvData.Instance.boundInvData.itemStack.itemValue; return false; } return true; } [HarmonyPatch(typeof(Inventory), nameof(Inventory.holdingItemStack), MethodType.Getter)] [HarmonyPrefix] public static bool Prefix_holdingItemStack_Inventory(ItemInventoryData[] ___slots, int ___m_HoldingItemIdx, ref ItemStack __result) { if (___slots[___m_HoldingItemIdx] is IModuleContainerFor multiInvData && multiInvData.Instance.useBound) { __result = multiInvData.Instance.boundInvData.itemStack; return false; } return true; } [HarmonyPatch(typeof(Inventory), "Item", MethodType.Getter)] [HarmonyPrefix] public static bool Prefix_Item_Inventory(ItemInventoryData[] ___slots, int _idx, ref ItemValue __result) { if (___slots[_idx] is IModuleContainerFor multiInvData && multiInvData.Instance.useBound) { __result = multiInvData.Instance.boundInvData.itemStack.itemValue; return false; } return true; } [HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.SwapSelectedAmmo))] [HarmonyPrefix] private static bool Prefix_SwapSelectedAmmo_ItemActionRanged(EntityAlive _entity, int _ammoIndex) { if (_entity.inventory?.holdingItemData is IModuleContainerFor dataModule && dataModule.Instance.itemModule.IsBoundActionRunning(dataModule.Instance)) { return false; } return true; } [HarmonyPatch(typeof(PlayerMoveController), nameof(PlayerMoveController.Update))] [HarmonyTranspiler] private static IEnumerable Transpiler_Update_PlayerMoveController(IEnumerable instructions) { var codes = instructions.ToList(); for (int i = 0; i < codes.Count; i++) { //ItemAction if (codes[i].opcode == OpCodes.Stloc_S && ((LocalBuilder)codes[i].operand).LocalIndex == 39) { codes.InsertRange(i + 1, new[] { new CodeInstruction(OpCodes.Ldarg_0), CodeInstruction.Call(typeof(MultiItemPatches), nameof(CheckAltMelee)) }); break; } } return codes; } private static void CheckAltMelee(PlayerMoveController controller) { if (DroneManager.Debug_LocalControl || !controller.gameManager.gameStateManager.IsGameStarted() || GameStats.GetInt(EnumGameStats.GameState) != 1) return; bool isUIOpen = controller.windowManager.IsCursorWindowOpen() || controller.windowManager.IsInputActive() || controller.windowManager.IsModalWindowOpen(); if (isUIOpen || controller.entityPlayerLocal.emodel.IsRagdollActive || controller.entityPlayerLocal.IsDead() || controller.entityPlayerLocal.AttachedToEntity != null) { return; } EntityPlayerLocal player = controller.entityPlayerLocal; if (PlayerActionKFLib.Instance.Enabled && (PlayerActionKFLib.Instance.AltMelee.WasPressed || PlayerActionKFLib.Instance.AltMelee.WasReleased) && player.inventory.holdingItemData is IModuleContainerFor dataModule) { bool isReloading = player.IsReloading(); int actionIndex = MultiActionManager.GetActionIndexForEntity(player); var reloadModule = player.inventory.holdingItem.Actions[actionIndex] as IModuleContainerFor; MultiItemInvData multiInvData = dataModule.Instance; if (multiInvData.itemModule.BoundItemClass?.Actions?[0] is ItemActionDynamicMelee dynamicAction && !multiInvData.itemModule.IsBoundActionRunning(multiInvData) && ((player.inventory.IsHoldingGun() && (!isReloading || reloadModule != null)) || !player.inventory.IsHoldingItemActionRunning()) && !player.AimingGun && !multiInvData.originalData.IsAnyActionLocked()) { multiInvData.useBound = true; multiInvData.itemModule.SetBoundParams(player.MinEventContext, multiInvData); bool canRun = dynamicAction.canStartAttack(multiInvData.boundInvData.actionData[0] as ItemActionDynamicMelee.ItemActionDynamicMeleeData); multiInvData.itemModule.RestoreParams(player.MinEventContext, multiInvData); multiInvData.useBound = false; if (!canRun) { return; } if (isReloading) { var itemActionData = player.inventory.holdingItemData.actionData[actionIndex] as ItemActionRanged.ItemActionDataRanged; itemActionData.m_LastShotTime = Time.time; reloadModule.Instance.Postfix_ExecuteAction(itemActionData, ((IModuleContainerFor)itemActionData).Instance, new ActionModuleInterruptReload.State { executed = true, lastShotTime = -1, isReloading = true, isWeaponReloading = true, }); } player.emodel.avatarController.UpdateBool(AvatarController.reloadHash, false); multiInvData.boundInvData.itemStack.itemValue.UseTimes = 0; multiInvData.useBound = true; multiInvData.itemModule.SetBoundParams(player.MinEventContext, multiInvData); multiInvData.itemModule.BoundItemClass.ExecuteAction(0, multiInvData.boundInvData, PlayerActionKFLib.Instance.AltMelee.WasReleased, controller.playerInput); player.emodel.avatarController.UpdateBool("UseAltMelee", true); multiInvData.itemModule.RestoreParams(player.MinEventContext, multiInvData); multiInvData.useBound = false; } } } //[HarmonyPatch(typeof(Animator), nameof(Animator.ResetTrigger), new[] {typeof(string)})] //[HarmonyPrefix] //private static void Prefix(Animator __instance, string name) //{ // if (name == "PowerAttack" && __instance.GetBool(name)) // { // Log.Warning($"RESETTING POWER ATTACK TRIGGER\n{StackTraceUtility.ExtractStackTrace()}"); // } //} }