using HarmonyLib; using KFCommonUtilityLib; using KFCommonUtilityLib.Scripts.Attributes; using KFCommonUtilityLib.Scripts.StaticManagers; using System.Collections.Generic; using UnityEngine; [TypeTarget(typeof(ItemActionZoom)), ActionDataTarget(typeof(ProceduralAimingData))] public class ActionModuleProceduralAiming { [HarmonyPatch(nameof(ItemAction.OnModificationsChanged)), MethodTargetPostfix] public void Postfix_OnModificationsChanged(ItemActionZoom __instance, ItemActionData _data, ProceduralAimingData __customData) { if (_data is IModuleContainerFor dataModule) { __customData.zoomInTime = dataModule.Instance.module.zoomInTimeBase / dataModule.Instance.module.aimSpeedModifierBase; __customData.ergoData = dataModule.Instance; } else { float zoomInTimeBase = 0.3f; __instance.Properties.ParseFloat("ZoomInTimeBase", ref zoomInTimeBase); float aimSpeedModifierBase = 1f; __instance.Properties.ParseFloat("AimSpeedModifierBase", ref aimSpeedModifierBase); __customData.zoomInTime = zoomInTimeBase / aimSpeedModifierBase; __customData.ergoData = null; } __customData.playerOriginTransform = null; __customData.playerCameraPosRef = _data.invData.holdingEntity is EntityPlayerLocal player && player.bFirstPersonView ? player.cameraTransform : null; var targets = AnimationRiggingManager.GetRigTargetsFromPlayer(_data.invData.holdingEntity); if (__customData.playerCameraPosRef) { if (targets.ItemFpv) { if (targets is RigTargets) { __customData.isRigWeapon = true; __customData.playerOriginTransform = targets.ItemAnimator.transform; __customData.rigWeaponLocalPosition = __customData.playerOriginTransform.localPosition; __customData.rigWeaponLocalRotation = __customData.playerOriginTransform.localRotation; } else { __customData.isRigWeapon = false; __customData.playerOriginTransform = __customData.playerCameraPosRef.FindInAllChildren("Hips"); } __customData.playerCameraPosRef = targets.ItemFpv.Find("PlayerCameraPositionReference"); } else { __customData.playerCameraPosRef = null; } } if (__customData.playerCameraPosRef) { __customData.aimRefTransform = targets.ItemFpv.Find("ScopeBasePositionReference"); if (__customData.aimRefTransform) { var scopeRefTrans = __customData.aimRefTransform.Find("ScopePositionReference"); if (!scopeRefTrans) { scopeRefTrans = new GameObject("ScopePositionReference").transform; scopeRefTrans.SetParent(__customData.aimRefTransform, false); } scopeRefTrans.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); scopeRefTrans.localScale = Vector3.one; __customData.aimRefTransform = scopeRefTrans; } } else { __customData.aimRefTransform = null; } __customData.ResetAiming(); __customData.UpdateCurrentReference(true); } [HarmonyPatch(nameof(ItemAction.StopHolding)), MethodTargetPostfix] public void Postfix_StopHolding(ProceduralAimingData __customData) { __customData.ResetAiming(); } //[HarmonyPatch(nameof(ItemAction.ExecuteAction)), MethodTargetPostfix] //public void Postfix_ExecuteAction(ProceduralAimingData __customData, ItemActionData _actionData) //{ // if (__customData.isAiming != ((ItemActionZoom.ItemActionDataZoom)_actionData).aimingValue) // { // __customData.UpdateCurrentReference(); // __customData.isAiming = ((ItemActionZoom.ItemActionDataZoom)_actionData).aimingValue; // } //} public class ProceduralAimingData { public ActionModuleErgoAffected.ErgoData ergoData; public float zoomInTime; public Transform aimRefTransform; public Transform playerCameraPosRef; public Transform playerOriginTransform; public bool isRigWeapon; public Vector3 rigWeaponLocalPosition; public Quaternion rigWeaponLocalRotation; public bool isAiming; public int curAimRefIndex = -1; //move curAimRefOffset towards aimRefOffset first, then move curAimOffset towards curAimRefOffset public Vector3 aimRefPosOffset; public Quaternion aimRefRotOffset; public Vector3 curAimPosOffset; public Quaternion curAimRotOffset; private Vector3 curAimPosVelocity; private Quaternion curAimRotVelocity; private Vector3 targetSwitchPosVelocity; private Quaternion targetSwitchRotVelocity; public List registeredReferences = new List(); private EntityPlayerLocal holdingEntity; private int CurAimRefIndex { get { for (int i = registeredReferences.Count - 1; i >= 0; i--) { if (registeredReferences[i].gameObject.activeInHierarchy) { return i; } } return -1; } } private AimReference CurAimRef => curAimRefIndex >= 0 && curAimRefIndex < registeredReferences.Count ? registeredReferences[curAimRefIndex] : null; public ProceduralAimingData(ItemInventoryData _invData, int _indexInEntityOfAction, ActionModuleProceduralAiming _module) { holdingEntity = _invData.holdingEntity as EntityPlayerLocal; } public void ResetAiming() { isAiming = false; curAimRefIndex = -1; aimRefPosOffset = Vector3.zero; aimRefRotOffset = Quaternion.identity; if (aimRefTransform) { aimRefTransform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); } curAimPosOffset = Vector3.zero; curAimRotOffset = Quaternion.identity; curAimPosVelocity = Vector3.zero; curAimRotVelocity = Quaternion.identity; targetSwitchPosVelocity = Vector3.zero; targetSwitchRotVelocity = Quaternion.identity; if (isRigWeapon && playerOriginTransform) { playerOriginTransform.localPosition = rigWeaponLocalPosition; playerOriginTransform.localRotation = rigWeaponLocalRotation; } } public bool RegisterGroup(AimReference[] group, string name) { if (holdingEntity && holdingEntity.bFirstPersonView) { foreach (var reference in group) { if (reference.index == -1) { reference.index = registeredReferences.Count; registeredReferences.Add(reference); } } UpdateCurrentReference(); //Log.Out($"Register group {name}\n{StackTraceUtility.ExtractStackTrace()}"); return true; } return false; } public void UpdateCurrentReference(bool snapTo = false) { curAimRefIndex = CurAimRefIndex; AimReference curAimRef = CurAimRef; if (aimRefTransform && curAimRef) { aimRefPosOffset = curAimRef.positionOffset; aimRefRotOffset = curAimRef.rotationOffset; if (curAimRef.asReference) { aimRefPosOffset -= Vector3.Project(aimRefPosOffset - aimRefTransform.parent.InverseTransformPoint(playerCameraPosRef.position), aimRefRotOffset * Vector3.forward); } if (snapTo) { aimRefTransform.localPosition = aimRefPosOffset; aimRefTransform.localRotation = aimRefRotOffset; } } for (int i = 0; i < registeredReferences.Count; i++) { registeredReferences[i].UpdateEnableState(isAiming && curAimRefIndex == i); } } public void LateUpdateAiming() { if (aimRefTransform && playerCameraPosRef && playerOriginTransform && CurAimRef) { if (isRigWeapon) { playerOriginTransform.SetLocalPositionAndRotation(rigWeaponLocalPosition, rigWeaponLocalRotation); } float zoomInTimeMod = ergoData == null ? zoomInTime : zoomInTime / ergoData.ModifiedErgo; zoomInTimeMod *= 0.25f; //move aimRef towards target aimRefTransform.localPosition = Vector3.SmoothDamp(aimRefTransform.localPosition, aimRefPosOffset, ref targetSwitchPosVelocity, 0.075f); aimRefTransform.localRotation = QuaternionUtil.SmoothDamp(aimRefTransform.localRotation, aimRefRotOffset, ref targetSwitchRotVelocity, 0.075f); //calculate current target aim offset Vector3 aimTargetPosOffset = playerCameraPosRef.InverseTransformDirection(playerCameraPosRef.position - aimRefTransform.position); Quaternion aimTargetRotOffset = playerCameraPosRef.localRotation * Quaternion.Inverse(aimRefTransform.parent.localRotation * aimRefTransform.localRotation); //move current aim offset towards target aim offset if (isAiming) { curAimPosOffset = Vector3.SmoothDamp(curAimPosOffset, aimTargetPosOffset, ref curAimPosVelocity, zoomInTimeMod); curAimRotOffset = QuaternionUtil.SmoothDamp(curAimRotOffset, aimTargetRotOffset, ref curAimRotVelocity, zoomInTimeMod); } else { curAimPosOffset = Vector3.SmoothDamp(curAimPosOffset, Vector3.zero, ref curAimPosVelocity, zoomInTimeMod); curAimRotOffset = QuaternionUtil.SmoothDamp(curAimRotOffset, Quaternion.identity, ref curAimRotVelocity, zoomInTimeMod); } //apply offset to player if (isRigWeapon) { (playerCameraPosRef.parent.rotation * curAimRotOffset * Quaternion.Inverse(playerCameraPosRef.parent.rotation)).ToAngleAxis(out var angle, out var axis); playerOriginTransform.RotateAround(aimRefTransform.position, axis, angle); playerOriginTransform.position += playerCameraPosRef.TransformDirection(curAimPosOffset); } else { playerOriginTransform.position += playerCameraPosRef.TransformDirection(curAimPosOffset); (playerCameraPosRef.parent.rotation * curAimRotOffset * Quaternion.Inverse(playerCameraPosRef.parent.rotation)).ToAngleAxis(out var angle, out var axis); playerOriginTransform.RotateAround(aimRefTransform.position, axis, angle); } } } } } [HarmonyPatch] public static class ProceduralAimingPatches { [HarmonyPatch(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.LateUpdate))] [HarmonyPostfix] private static void Postfix_LateUpdate_EntityPlayerLocal(EntityPlayerLocal __instance) { if (__instance.inventory?.holdingItemData?.actionData?[1] is IModuleContainerFor module) { if (__instance.AimingGun != module.Instance.isAiming) { module.Instance.isAiming = __instance.AimingGun; module.Instance.UpdateCurrentReference(true); } module.Instance.LateUpdateAiming(); } } }