Files
7d2dXG/Mods/0A-KFCommonUtilityLib/Scripts/Items/ModularActions/ActionModuleProceduralRecoil.cs
Nathaniel Cosford 062dfab2cd Patched
2025-05-30 01:04:40 +09:30

1059 lines
51 KiB
C#

using KFCommonUtilityLib.Scripts.Attributes;
using HarmonyLib;
using UnityEngine;
using KFCommonUtilityLib.Scripts.StaticManagers;
using KFCommonUtilityLib;
using System.Reflection.Emit;
using System.Collections.Generic;
using UniLinq;
using static ActionModuleTagged;
using KFCommonUtilityLib.Scripts.Utilities;
public struct ShotIndexRange
{
public IntRange IndexRange;
public Vector2 RecoilPositionStrength;
public Vector2 RecoilRotationStrength;
public Vector2 RecoilAngleRange;
public static ShotIndexRange Parse(DynamicProperties prop)
{
var ret = new ShotIndexRange();
StringParsers.TryParseRange(prop.GetString("IndexRange"), out ret.IndexRange);
prop.ParseVec("RecoilPositionStrength", ref ret.RecoilPositionStrength);
prop.ParseVec("RecoilRotationStrength", ref ret.RecoilRotationStrength);
prop.ParseVec("RecoilAngleRange", ref ret.RecoilAngleRange);
return ret;
}
}
public class ShotIndexRangeGroup
{
private ShotIndexRange[] shotRange;
private ShotIndexRangeGroup()
{
}
public static ShotIndexRangeGroup Parse(DynamicProperties props)
{
ShotIndexRangeGroup group = new ShotIndexRangeGroup();
List<ShotIndexRange> list = new List<ShotIndexRange>();
for (int i = 0; i < 99; i++)
{
if (props.Classes.TryGetValue($"ShotsGroupSettings{i}", out var shotProps))
{
list.Add(ShotIndexRange.Parse(shotProps));
}
else
{
break;
}
}
if (list.Count > 0)
{
group.shotRange = list.ToArray();
}
return group;
}
public bool FindIndexGroup(int index, out ShotIndexRange range)
{
range = default;
if (shotRange == null)
{
return false;
}
foreach (var shotRange in shotRange)
{
if (shotRange.IndexRange.min <= index && shotRange.IndexRange.max >= index)
{
range = shotRange;
return true;
}
}
return false;
}
}
[TypeTarget(typeof(ItemActionRanged)), TypeDataTarget(typeof(EFTProceduralRecoilData))]
public class ActionModuleProceduralRecoil
{
//======
public const float BASE_RECOIL_ROTATION_STR_MIN = 0.9f;
public const float BASE_RECOIL_ROTATION_STR_MAX = 1.15f;
public const float BASE_RECOIL_POSITION_STR_MIN = 0.65f;
public const float BASE_RECOIL_POSITION_STR_MAX = 1.05f;
public const float CONSTANT_ROTATION_STR_MULTIPLIER = 0.1399f;
public const float INTENSITY_MULTIPLIER_CROUCHING = 0.85f;
public struct RecoilPassiveTags
{
public FastTags<TagGroup.Global> WeaponRecoilModifier;
public FastTags<TagGroup.Global> DeltaAnglePerShot;
public FastTags<TagGroup.Global> DeltaAngleMin;
public FastTags<TagGroup.Global> DeltaAngleMax;
public FastTags<TagGroup.Global> CameraRecoilConversionPerc;
public FastTags<TagGroup.Global> WeaponRotIntensity;
public FastTags<TagGroup.Global> WeaponPosIntensity;
public FastTags<TagGroup.Global> CameraRotIntensity;
public FastTags<TagGroup.Global> WeaponRotIntensityMultiplier;
public FastTags<TagGroup.Global> WeaponRotationForceDamping;
public FastTags<TagGroup.Global> WeaponRotationForceReturnSpeed;
public FastTags<TagGroup.Global> WeaponPositionForceDamping;
public FastTags<TagGroup.Global> WeaponPositionForceReturnSpeed;
public FastTags<TagGroup.Global> CameraRotationForceDamping;
public FastTags<TagGroup.Global> CameraRotationForceReturnSpeed;
public FastTags<TagGroup.Global> RecoilReturnBias;
public FastTags<TagGroup.Global> RecoilReturnBiasDamping;
public FastTags<TagGroup.Global> RecoilForceStrength;
public FastTags<TagGroup.Global> RecoilAimingIntensity;
}
public static readonly FastTags<TagGroup.Global> WeaponRecoilModifer = FastTags<TagGroup.Global>.Parse("RecoilIntensityModifier");
public static readonly FastTags<TagGroup.Global> DeltaAnglePerShot = FastTags<TagGroup.Global>.Parse("RecoilStableAngleIncreaseStep");
public static readonly FastTags<TagGroup.Global> DeltaAngleMin = FastTags<TagGroup.Global>.Parse("ProgressRecoilAngleOnStableMin");
public static readonly FastTags<TagGroup.Global> DeltaAngleMax = FastTags<TagGroup.Global>.Parse("ProgressRecoilAngleOnStableMax");
public static readonly FastTags<TagGroup.Global> CameraRecoilConversionPerc = FastTags<TagGroup.Global>.Parse("RecoilCamera");
public static readonly FastTags<TagGroup.Global> WeaponRotIntensity = FastTags<TagGroup.Global>.Parse("WeaponRotIntensity");
public static readonly FastTags<TagGroup.Global> WeaponPosIntensity = FastTags<TagGroup.Global>.Parse("WeaponPosIntensity");
public static readonly FastTags<TagGroup.Global> CameraRotIntensity = FastTags<TagGroup.Global>.Parse("CameraRotIntensity");
public static readonly FastTags<TagGroup.Global> WeaponRotIntensityMultiplier = FastTags<TagGroup.Global>.Parse("RecoilCategoryMultiplierHandRotation");
public static readonly FastTags<TagGroup.Global> WeaponRotationForceDamping = FastTags<TagGroup.Global>.Parse("RecoilDampingHandRotation");
public static readonly FastTags<TagGroup.Global> WeaponRotationForceReturnSpeed = FastTags<TagGroup.Global>.Parse("RecoilReturnSpeedHandRotation");
public static readonly FastTags<TagGroup.Global> WeaponPositionForceDamping = FastTags<TagGroup.Global>.Parse("RecoilDampingHandPosition");
public static readonly FastTags<TagGroup.Global> WeaponPositionForceReturnSpeed = FastTags<TagGroup.Global>.Parse("RecoilReturnSpeedHandPosition");
public static readonly FastTags<TagGroup.Global> CameraRotationForceDamping = FastTags<TagGroup.Global>.Parse("RecoilDampingCameraRotation");
public static readonly FastTags<TagGroup.Global> CameraRotationForceReturnSpeed = FastTags<TagGroup.Global>.Parse("RecoilReturnSpeedCameraRotation");
public static readonly FastTags<TagGroup.Global> RecoilReturnBias = FastTags<TagGroup.Global>.Parse("RecoilReturnPathOffsetHandRotation");
public static readonly FastTags<TagGroup.Global> RecoilReturnBiasDamping = FastTags<TagGroup.Global>.Parse("RecoilReturnPathDampingHandRotation");
public static readonly FastTags<TagGroup.Global> RecoilAimingIntensity = FastTags<TagGroup.Global>.Parse("RecoilAimingIntensity");
public RecoilPassiveTags tags;
[HarmonyPatch(nameof(ItemAction.ReadFrom)), MethodTargetPostfix]
public void Postfix_ReadFrom(DynamicProperties _props, ItemAction __instance)
{
var itemTags = __instance.item.ItemTags;
tags = new RecoilPassiveTags()
{
WeaponRecoilModifier = itemTags | WeaponRecoilModifer,
DeltaAnglePerShot = itemTags | DeltaAnglePerShot,
DeltaAngleMin = itemTags | DeltaAngleMin,
DeltaAngleMax = itemTags | DeltaAngleMax,
CameraRecoilConversionPerc = itemTags | CameraRecoilConversionPerc,
WeaponRotIntensity = itemTags | WeaponRotIntensity,
WeaponPosIntensity = itemTags | WeaponPosIntensity,
CameraRotIntensity = itemTags | CameraRotIntensity,
WeaponRotIntensityMultiplier = itemTags | WeaponRotIntensityMultiplier,
WeaponRotationForceDamping = itemTags | WeaponRotationForceDamping,
WeaponRotationForceReturnSpeed = itemTags | WeaponRotationForceReturnSpeed,
WeaponPositionForceDamping = itemTags | WeaponPositionForceDamping,
WeaponPositionForceReturnSpeed = itemTags | WeaponPositionForceReturnSpeed,
CameraRotationForceDamping = itemTags | CameraRotationForceDamping,
CameraRotationForceReturnSpeed = itemTags | CameraRotationForceReturnSpeed,
RecoilReturnBias = itemTags | RecoilReturnBias,
RecoilReturnBiasDamping = itemTags | RecoilReturnBiasDamping,
RecoilForceStrength = FastTags<TagGroup.Global>.Parse("RecoilForceStrength"),
RecoilAimingIntensity = itemTags | RecoilAimingIntensity
};
}
[HarmonyPatch(nameof(ItemAction.OnModificationsChanged)), MethodTargetPostfix]
public void Postfix_OnModificationChanged(ItemActionRanged __instance, ItemActionData _data, EFTProceduralRecoilData __customData)
{
Vector2 recoilForce = default;
string originalValue = "0,0";
DynamicProperties props = __instance.Properties;
props.ParseString("WeaponRecoilForce", ref originalValue);
recoilForce = StringParsers.ParseVector2(_data.invData.itemValue.GetPropertyOverrideForAction("WeaponRecoilForce", originalValue, __instance.ActionIndex));
__customData.WeaponRecoilForceUp = recoilForce.x;
__customData.WeaponRecoilForceBack = recoilForce.y;
originalValue = "80,100";
props.ParseString("RecoilAngleRange", ref originalValue);
__customData.BaseRecoilRadianRange = StringParsers.ParseVector2(_data.invData.itemValue.GetPropertyOverrideForAction("RecoilAngleRange", originalValue, __instance.ActionIndex)) * Mathf.Deg2Rad;
originalValue = "5";
props.ParseString("StableShotIndex", ref originalValue);
__customData.StableShotIndex = StringParsers.ParseSInt32(_data.invData.itemValue.GetPropertyOverrideForAction("StableShotIndex", originalValue, __instance.ActionIndex));
originalValue = "true";
props.ParseString("RampUpRecoil", ref originalValue);
__customData.RampRecoil = StringParsers.ParseBool(_data.invData.itemValue.GetPropertyOverrideForAction("RampUpRecoil", originalValue, __instance.ActionIndex));
originalValue = "3";
props.ParseString("RampRecoilIndex", ref originalValue);
__customData.RampRecoilIndex = StringParsers.ParseSInt32(_data.invData.itemValue.GetPropertyOverrideForAction("RampRecoilIndex", originalValue, __instance.ActionIndex));
__customData.ShotRangeGroup = ShotIndexRangeGroup.Parse(props);
__customData.playerOriginTransform = null;
if (_data.invData.holdingEntity is EntityPlayerLocal player && player.bFirstPersonView)
{
__customData.playerCameraTransform = player.cameraTransform;
var targets = AnimationRiggingManager.GetRigTargetsFromPlayer(_data.invData.holdingEntity);
__customData.recoilPivotTransform = null;
__customData.hasPivotOverride = false;
if (targets)
{
__customData.recoilPivotTransform = AnimationRiggingManager.GetAddPartTransformOverride(targets.transform, "RecoilPivot");
}
if (__customData.recoilPivotTransform)
{
__customData.hasPivotOverride = true;
}
else
{
__customData.recoilPivotTransform = player.cameraTransform.FindInAllChildren("RightHand");
}
if (targets && targets.ItemFpv && targets is RigTargets)
{
__customData.playerOriginTransform = targets.ItemAnimator.transform;
__customData.isRigWeapon = true;
}
else
{
__customData.playerOriginTransform = player.cameraTransform.FindInAllChildren("Hips");
__customData.isRigWeapon = false;
}
var oldRecoil = targets.ItemAnimator?.GetComponent<AnimationRandomRecoil>();
if (oldRecoil)
{
oldRecoil.enabled = false;
}
}
__customData.ResetRecoil();
if (!EFTProceduralRecoilData.dontUpdateParam || (EFTProceduralRecoilData.dontUpdateParam && !__customData.passivesInited))
{
CalcRecoilParams(__customData, _data as ItemActionRanged.ItemActionDataRanged);
__customData.passivesInited = true;
}
__customData.isHolding = true;
//CalcDampFactors(__customData, _data as ItemActionRanged.ItemActionDataRanged);
}
[HarmonyPatch(nameof(ItemAction.StopHolding)), MethodTargetPostfix]
public void Postfix_StopHolding(EFTProceduralRecoilData __customData)
{
__customData.ResetRecoil();
__customData.isHolding = false;
}
//[HarmonyPatch(nameof(ItemAction.OnHoldingUpdate)), MethodTargetPostfix]
//public void Postfix_OnHoldingUpdate(EFTProceduralRecoilData __customData, ItemActionData _actionData)
//{
// if (_actionData.invData.holdingEntity is EntityPlayerLocal player && player.bFirstPersonView)
// {
// bool aimingGun = player.AimingGun;
// if (aimingGun)
// {
// __customData.WeaponRotIntensity = 0.75f;
// //__customData.WeaponPosIntensity = 0f;
// }
// else
// {
// __customData.WeaponRotIntensity = 1f;
// //__customData.WeaponPosIntensity = 1f;
// }
// }
//}
[HarmonyPatch(nameof(ItemActionRanged.onHoldingEntityFired)), MethodTargetPostfix]
public void Postfix_onHoldingEntityFired(ItemActionData _actionData, EFTProceduralRecoilData __customData)
{
if (_actionData.invData.holdingEntity is EntityPlayerLocal player && player.bFirstPersonView)
{
if (!EFTProceduralRecoilData.dontUpdateParam)
{
CalcRecoilParams(__customData, __customData.rangedData);
}
__customData.AddRecoilForce(EffectManager.GetValue(CustomEnums.CustomTaggedEffect, _actionData.invData.itemValue, 1, player, null, tags.RecoilForceStrength, false, true, false, false, false, 1, false, false));
}
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.fireShot)), MethodTargetTranspiler]
public static IEnumerable<CodeInstruction> Transpiler_ItemActionRanged_fireShot(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var mtd_ray = AccessTools.Method(typeof(EntityAlive), nameof(EntityAlive.GetLookRay));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(mtd_ray))
{
codes.RemoveRange(i - 1, 2);
codes.InsertRange(i - 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_2),
CodeInstruction.Call(typeof(ActionModuleProceduralRecoil), nameof(GetLookRayOverride))
});
break;
}
}
return codes;
}
[HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemActionLauncher.getImageActionEffectsStartPosAndDirection)), MethodTargetTranspiler]
public static IEnumerable<CodeInstruction> Transpiler_ItemActionLauncher_getImageActionEffectsStartPosAndDirection(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var mtd_ray = AccessTools.Method(typeof(EntityAlive), nameof(EntityAlive.GetLookRay));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(mtd_ray))
{
codes.RemoveRange(i - 3, 4);
codes.InsertRange(i - 3, new[]
{
new CodeInstruction(OpCodes.Ldarg_1),
CodeInstruction.Call(typeof(ActionModuleProceduralRecoil), nameof(GetLookRayOverride))
});
break;
}
}
return codes;
}
public static Ray GetLookRayOverride(ItemActionData data)
{
if (data.invData.holdingEntity is EntityPlayerLocal player && player.bFirstPersonView && data is IModuleContainerFor<EFTProceduralRecoilData> dataModule && data.invData.holdingEntity.AimingGun)
{
var aimingModule = (data.invData.actionData[1] as IModuleContainerFor<ActionModuleProceduralAiming.ProceduralAimingData>)?.Instance;
if (aimingModule != null)
{
Transform transform = aimingModule.CurAimRef.transform;
return new Ray(transform.position + Origin.position, transform.forward);
}
}
return data.invData.holdingEntity.GetLookRay();
}
public void CalcRecoilParams(EFTProceduralRecoilData recoilData, ItemActionRanged.ItemActionDataRanged rangedData)
{
recoilData.WeaponRecoilModifier = Mathf.Max(0, EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 1, rangedData.invData.holdingEntity, null, tags.WeaponRecoilModifier));
recoilData.BaseWeaponRecoilStrRot = new Vector2(BASE_RECOIL_ROTATION_STR_MIN, BASE_RECOIL_ROTATION_STR_MAX) * (recoilData.WeaponRecoilForceUp * recoilData.WeaponRecoilModifier + 20) * CONSTANT_ROTATION_STR_MULTIPLIER;
recoilData.BaseWeaponRecoilStrPos = new Vector2(BASE_RECOIL_POSITION_STR_MIN, BASE_RECOIL_POSITION_STR_MAX) * (recoilData.WeaponRecoilForceBack * recoilData.WeaponRecoilModifier + 20) * CONSTANT_ROTATION_STR_MULTIPLIER;
recoilData.DeltaAnglePerShot = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 2.5f, rangedData.invData.holdingEntity, null, tags.DeltaAnglePerShot);
recoilData.DeltaAngleRange.x = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 0, rangedData.invData.holdingEntity, null, tags.DeltaAngleMin);
recoilData.DeltaAngleRange.y = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 30, rangedData.invData.holdingEntity, null, tags.DeltaAngleMax);
recoilData.CameraRecoilConversionPerc = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 0.04f, rangedData.invData.holdingEntity, null, tags.CameraRecoilConversionPerc);
recoilData.WeaponRotIntensity = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 1, rangedData.invData.holdingEntity, null, tags.WeaponRotIntensity);
recoilData.WeaponPosIntensity = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 1, rangedData.invData.holdingEntity, null, tags.WeaponPosIntensity);
recoilData.CameraRotIntensity = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 1, rangedData.invData.holdingEntity, null, tags.CameraRotIntensity);
recoilData.WeaponRotIntensityMultiplier = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 0.2f, rangedData.invData.holdingEntity, null, tags.WeaponRotIntensityMultiplier);
recoilData.WeaponRotationForceDamping = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 0.85f, rangedData.invData.holdingEntity, null, tags.WeaponRotationForceDamping);
recoilData.WeaponRotationForceReturnSpeed = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 3, rangedData.invData.holdingEntity, null, tags.WeaponRotationForceReturnSpeed);
recoilData.WeaponPositionForceDamping = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 0.5f, rangedData.invData.holdingEntity, null, tags.WeaponPositionForceDamping);
recoilData.WeaponPositionForceReturnSpeed = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 0.08f, rangedData.invData.holdingEntity, null, tags.WeaponPositionForceReturnSpeed);
recoilData.CameraRotationForceDamping = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 0.5f, rangedData.invData.holdingEntity, null, tags.CameraRotationForceDamping);
recoilData.CameraRotationForceReturnSpeed = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 0.05f, rangedData.invData.holdingEntity, null, tags.CameraRotationForceReturnSpeed);
recoilData.RecoilReturnBias = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 0.01f, rangedData.invData.holdingEntity, null, tags.RecoilReturnBias);
recoilData.BiasDamping = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 0.48f, rangedData.invData.holdingEntity, null, tags.RecoilReturnBiasDamping);
recoilData.HandRotValueAimIntensity = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, rangedData.invData.itemValue, 1f, rangedData.invData.holdingEntity, null, tags.RecoilAimingIntensity);
}
//public void CalcDampFactors(ProceduralRecoilData recoilData, ItemActionRanged.ItemActionDataRanged rangedData)
//{
// recoilData.recoilFollowDampPerc = EffectManager.GetValue(CustomEnums.PRDampPerc, rangedData.invData.itemValue, 0f, rangedData.invData.holdingEntity);
// recoilData.recoilFollowReturnSpeed = EffectManager.GetValue(CustomEnums.PRReturnSpeed, rangedData.invData.itemValue, 1f, rangedData.invData.holdingEntity);
//}
public class EFTProceduralRecoilData
{
public ItemActionRanged.ItemActionDataRanged rangedData;
public ActionModuleProceduralRecoil module;
public ItemInventoryData invData;
public Transform playerOriginTransform;
public Transform recoilPivotTransform;
public Transform playerCameraTransform;
public bool isRigWeapon;
public bool hasPivotOverride;
public int actionIndex;
public bool isHolding;
public static bool dontUpdateParam;
public bool passivesInited;
// initial weapon recoil property
public float WeaponRecoilForceUp, WeaponRecoilForceBack;
public ShotIndexRangeGroup ShotRangeGroup;
//======
// the recoil angle range in radian, where 90 is up and 0 is right
public Vector2 BaseRecoilRadianRange;
// the amount of shots before recoil state is considered stable
public int StableShotIndex = 3;
// whether there is a ramping stage after reaching RampRecoilIndex
// where the recoil force is multiplied by clamp(1, StableShotIndex) / StableShotIndex
public bool RampRecoil = true;
public int RampRecoilIndex = 1;
//====== temp
// modifier for the weapon recoil force
public float WeaponRecoilModifier;
//======
// calculated basic weapon recoil values, multiplied by mod/perk modifiers
public Vector2 BaseWeaponRecoilStrRot, BaseWeaponRecoilStrPos;
// angle increase per shot
public float DeltaAnglePerShot;
// delta angle clamp value
public Vector2 DeltaAngleRange;
// the percentage of the recoil strength that is converted to camera kick,
// which in our case should also be applied inversely to the weapon
public float CameraRecoilConversionPerc;
// the intensity modifier of the accumulated recoil force
public float WeaponRotIntensity = 1, WeaponPosIntensity = 1, CameraRotIntensity = 1;
// the base intensity multiplier of incoming weapon rotation force
public float WeaponRotIntensityMultiplier;
// damping and return speed, only need to set for weapon rotation
public float WeaponRotationForceDamping, WeaponRotationForceReturnSpeed;
public float WeaponPositionForceDamping = 0.5f, WeaponPositionForceReturnSpeed = 0.08f;
public float CameraRotationForceDamping = 0.5f, CameraRotationForceReturnSpeed = 0.05f;
//======
// offset perc during recoil return to apply
public float RecoilReturnBias = 0.01f;
// recoil return bias damping
public float BiasDamping;
//======
// x -> up (negative) | y -> hor (positive = right) | z -> back (positive)
public Vector3 RecoilDirection;
//====== hand rotation fields
public bool IsStable;
public bool IsReturning;
public bool IsHandRotDirty;
public Vector2 CurrentStableRotationOffset;
public float RampMultiplier;
public Vector2 CurrentRotationOffset;
public float CurrentRotationAccumulated;
public float HandRotValueAimIntensity = 1f;
public Vector2 HandRotValueCur, HandRotValuePrev, HandRotValueApply, HandRotVelocity, HandRotForce;
public float HandRotValueXAfterRecoil = 0.01f;
public AnimationCurve HandRotReturnSpeedCurve = new AnimationCurve(new Keyframe(0, 0.008f, 0.0002f, 0.0002f) { inWeight = 0, outWeight = 0.0775f },
new Keyframe(2.5f, 0.008f, 0.0001f, 0.0001f) { inWeight = 0.0717f, outWeight = 0 })
{
preWrapMode = WrapMode.ClampForever,
postWrapMode = WrapMode.ClampForever
};
public float HandRotCurveTime;
public Vector2 HandRotStableOffsetRange = new Vector2(0.1f, 8f);
public Vector2 TargetStableRotationOffset;
public float AutoFireReturnSpeed;
public int LastReturnOffsetSign;
public float RecoilOffsetImpulse;
public float RecoilOffsetLerpSpeed = 0.01f;
//====== hand position fields
public bool IsHandPosDirty;
public float HandPosValue, HandPosVelocity, HandPosForce;
//====== cam rotation fields
public bool IsCamRotDirty;
public Vector2 CamRotValue, CamRotVelocity, CamRotForce;
public EFTProceduralRecoilData(ItemActionData actionData, ItemInventoryData _invData, int _indexInEntityOfAction, ActionModuleProceduralRecoil _module)
{
rangedData = actionData as ItemActionRanged.ItemActionDataRanged;
module = _module;
invData = _invData;
actionIndex = _indexInEntityOfAction;
}
public void AddRecoilForce(float forceStr = 1)
{
if (rangedData.curBurstCount >= StableShotIndex)
{
SetStable(true);
}
if (RampRecoil && rangedData.curBurstCount >= RampRecoilIndex)
{
RampMultiplier = Mathf.Clamp01((float)rangedData.curBurstCount / StableShotIndex);
}
else
{
RampMultiplier = 1;
}
CalcRecoilForceStr(forceStr, out float rotStr, out float posStr);
CalcRecoilDirRadian(out Vector2 dirRad);
CalcFinalRecoilDirection(dirRad, rotStr, posStr);
RedirectRecoilForceToHandRot();
RedirectRecoilForceToPosAndCam();
ProceduralRecoilUpdater.LastShotTime = Time.time + 0.2f;
}
public void ResetRecoil()
{
IsStable = false;
IsReturning = false;
IsHandRotDirty = false;
CurrentStableRotationOffset = Vector2.zero;
CurrentRotationOffset = Vector2.zero;
CurrentRotationAccumulated = 0;
HandRotValueCur = HandRotValuePrev = HandRotVelocity = HandRotForce = Vector2.zero;
HandRotValueXAfterRecoil = 0.01f;
HandRotCurveTime = 0f;
TargetStableRotationOffset = Vector2.zero;
AutoFireReturnSpeed = 0f;
LastReturnOffsetSign = 0;
IsHandPosDirty = false;
HandPosValue = HandPosVelocity = HandPosForce = 0f;
IsCamRotDirty = false;
CamRotValue = CamRotVelocity = CamRotForce = Vector2.zero;
//ProceduralRecoilUpdater.SetTargetRecoilPosOffset(Vector3.zero);
}
public void FixedUpdate(float dt)
{
if (!isHolding)
{
return;
}
if (rangedData.state == ItemActionFiringState.Off)
{
SetStable(false);
}
FixedUpdateHandRot(dt);
FixedUpdateHandPos(dt);
FixedUpdateCamRot(dt);
}
public void LateUpdate(float dt)
{
if (!isHolding)
{
return;
}
var aimingData = ((IModuleContainerFor<ActionModuleProceduralAiming.ProceduralAimingData>)invData.actionData[1]).Instance;
//ProceduralRecoilUpdater.InverseWorldCamKickOffsetCur.ToAngleAxis(out float camRotAngle, out Vector3 camRotAxis);
//playerOriginTransform.RotateAround(aimingData.aimRefTransform.position, camRotAxis, camRotAngle);
AimReference aimref = aimingData.CurAimRef;
Transform aimRefTransform = aimref?.transform ?? aimingData.aimRefTransform;
if (!aimRefTransform)
{
return;
}
Vector3 prevAimRefPos = aimRefTransform.position;
// right hand forward = right, right = -up, up = -forward
Vector3 targetHandPosOffset = -playerCameraTransform.forward * HandPosValue;
//Vector3 targetHandForward = Quaternion.AngleAxis(HandRotValueCur.x, playerCameraTransform.right)
// * (Quaternion.AngleAxis(HandRotValueCur.y, playerCameraTransform.up) * playerCameraTransform.forward);
//Quaternion.FromToRotation(playerCameraTransform.forward, targetHandForward).ToAngleAxis(out float worldAngleOffset, out Vector3 worldRotAxis);
((playerCameraTransform.rotation * Quaternion.Euler(HandRotValueApply)) * Quaternion.Inverse(playerCameraTransform.rotation)).ToAngleAxis(out float worldAngleOffset, out Vector3 worldRotAxis);
if (isRigWeapon && !hasPivotOverride)
{
playerOriginTransform.RotateAround(recoilPivotTransform.position, worldRotAxis, worldAngleOffset);
playerOriginTransform.position += targetHandPosOffset;
}
else
{
playerOriginTransform.position += targetHandPosOffset;
playerOriginTransform.RotateAround(recoilPivotTransform.position, worldRotAxis, worldAngleOffset);
}
Vector3 alignmentPos = aimref?.alignmentTarget?.position ?? (aimRefTransform.position - aimingData.AimRefOffset * aimRefTransform.forward);
((playerCameraTransform.rotation * Quaternion.Euler(ProceduralRecoilUpdater.CamAimRecoilRotOffsetCur)) * Quaternion.Inverse(playerCameraTransform.rotation)).ToAngleAxis(out worldAngleOffset, out worldRotAxis);
playerOriginTransform.RotateAround(alignmentPos, worldRotAxis, worldAngleOffset);
ProceduralRecoilUpdater.LateUpdateCamRecoilPosOffset(aimRefTransform.position - prevAimRefPos, dt, invData.holdingEntity.AimingGun);
playerOriginTransform.position -= ProceduralRecoilUpdater.GetCurRecoilPosOffset();
//ProceduralRecoilUpdater.InverseWorldCamKickOffsetCur.ToAngleAxis(out float camRotAngle, out Vector3 camRotAxis);
//playerOriginTransform.RotateAround(aimRefTransform.position - aimingData.AimRefOffset * aimRefTransform.forward, camRotAxis, camRotAngle);
}
private void FixedUpdateHandRot(float dt)
{
bool isAiming = invData.holdingEntity.AimingGun;
if (IsHandRotDirty)
{
float weaponRotIntensity = WeaponRotIntensity;
if (isAiming)
{
weaponRotIntensity *= 0.75f;
}
HandRotVelocity += HandRotForce * weaponRotIntensity;
HandRotForce = Vector2.zero;
HandRotCurveTime = Mathf.Min(HandRotValueCur.magnitude, HandRotCurveTime);
if (IsStable)
{
float stableOffset = Random.Range(HandRotStableOffsetRange.x, HandRotStableOffsetRange.y);
TargetStableRotationOffset = CurrentStableRotationOffset + HandRotVelocity * stableOffset;
Vector2 stableOffsetDir = TargetStableRotationOffset - HandRotValueCur;
if (stableOffsetDir.magnitude > 0.95f)
{
stableOffsetDir *= 0.95f;
TargetStableRotationOffset = HandRotValueCur + stableOffsetDir;
}
}
IsHandRotDirty = false;
}
if (rangedData.state == ItemActionFiringState.Off)
{
AutoFireReturnSpeed = WeaponRotationForceReturnSpeed * HandRotReturnSpeedCurve.Evaluate(HandRotCurveTime);
//mount?
}
if (IsHandRotDirty && rangedData.state != ItemActionFiringState.Off && !IsStable)
{
AutoFireReturnSpeed *= RampMultiplier;
}
HandRotCurveTime += dt;
HandRotVelocity *= WeaponRotationForceDamping;
if (!IsStable)
{
HandRotVelocity -= HandRotValueCur * AutoFireReturnSpeed;
HandRotValueCur += HandRotVelocity;
}
else
{
HandRotValueCur = HandRotValueCur + (TargetStableRotationOffset - HandRotValueCur) * 0.2f;
}
UpdateReturnBias(dt);
if (rangedData.state == ItemActionFiringState.Off)
{
if (HandRotValueXAfterRecoil != 0)
{
float speedDamp = Mathf.Abs(Mathf.Clamp(HandRotValueCur.x, HandRotValueXAfterRecoil, -0.01f) / HandRotValueXAfterRecoil);
RecoilOffsetLerpSpeed = Mathf.Clamp(1 - speedDamp, 0.01f, 1f) * 0.01f;
HandRotValueCur = Vector2.Lerp(HandRotValueCur, Vector2.zero, RecoilOffsetLerpSpeed);
//Log.Out($"LerpBack RecoilOffsetLerpSpeed {RecoilOffsetLerpSpeed} HandRotValueCurY {HandRotValueCur.y}");
}
}
HandRotValuePrev = HandRotValueCur;
if (isAiming)
{
//HandRotValueApply = Vector2.Lerp(HandRotValueApply, new Vector2(HandRotValueCur.x * HandRotValueAimIntensity, HandRotValueCur.y), 8 * dt);
HandRotValueApply = new Vector2(HandRotValueCur.x * HandRotValueAimIntensity, HandRotValueCur.y);
}
else
{
//HandRotValueApply = Vector2.Lerp(HandRotValueApply, HandRotValueCur, 8 * dt);
HandRotValueApply = HandRotValueCur;
}
}
private void FixedUpdateHandPos(float dt)
{
if (IsHandPosDirty)
{
HandPosVelocity += HandPosForce * WeaponPosIntensity;
HandPosForce = 0;
IsHandPosDirty = false;
}
HandPosVelocity -= HandPosValue * WeaponPositionForceReturnSpeed;
HandPosVelocity *= WeaponPositionForceDamping;
HandPosValue += HandPosVelocity;
}
private void FixedUpdateCamRot(float dt)
{
if (IsCamRotDirty)
{
CamRotVelocity += CamRotForce * CameraRotIntensity;
CamRotForce = Vector2.zero;
IsCamRotDirty = false;
}
CamRotVelocity -= CamRotValue * CameraRotationForceReturnSpeed;
CamRotVelocity *= CameraRotationForceDamping;
CamRotValue += CamRotVelocity;
}
public void SetStable(bool stable)
{
if (stable)
{
if (!IsStable)
{
IsStable = true;
CurrentStableRotationOffset = CurrentRotationOffset;
}
}
else if (IsStable)
{
CurrentRotationAccumulated = 0;
IsStable = false;
}
}
private void CalcRecoilForceStr(float forceStr, out float rotStr, out float posStr)
{
//todo: random range according to burst count
Vector2 rotRange = BaseWeaponRecoilStrRot;
Vector2 posRange = BaseWeaponRecoilStrPos;
if (ShotRangeGroup.FindIndexGroup(rangedData.curBurstCount, out var range))
{
rotRange += range.RecoilRotationStrength;
posRange += range.RecoilPositionStrength;
}
rotStr = Random.Range(rotRange.x, rotRange.y) * forceStr;
posStr = Random.Range(posRange.x, posRange.y) * forceStr;
}
private void CalcRecoilDirRadian(out Vector2 dirRad)
{
Vector2 randomRange = default;
if (ShotRangeGroup.FindIndexGroup(rangedData.curBurstCount, out var range))
{
randomRange = range.RecoilAngleRange;
}
if (IsStable)
{
CurrentRotationAccumulated = Mathf.Clamp(CurrentRotationAccumulated + DeltaAnglePerShot, DeltaAngleRange.x, DeltaAngleRange.y);
randomRange += new Vector2(CurrentRotationAccumulated, -CurrentRotationAccumulated);
}
dirRad = BaseRecoilRadianRange + randomRange * Mathf.Deg2Rad;
}
private void CalcFinalRecoilDirection(Vector2 recoilDirRad, float recoilRotStr, float recoilPosStr)
{
float recoilRad = Random.Range(recoilDirRad.x, recoilDirRad.y);
float poseFactor = rangedData.invData.holdingEntity.IsCrouching ? ActionModuleProceduralRecoil.INTENSITY_MULTIPLIER_CROUCHING : 1f;
RecoilDirection = new Vector3(-Mathf.Sin(recoilRad) * recoilRotStr * poseFactor, Mathf.Cos(recoilRad) * recoilRotStr * poseFactor, recoilPosStr * poseFactor); //aiming intensity?
}
private void RedirectRecoilForceToHandRot()
{
HandRotForce += new Vector2(RecoilDirection.x, RecoilDirection.y) * 0.15f * WeaponRotIntensityMultiplier * RampMultiplier; //perhaps mount multiplier in the future?
IsReturning = true;
if (IsStable)
{
HandRotVelocity = Vector2.zero;
}
IsHandRotDirty = true;
}
private void RedirectRecoilForceToPosAndCam()
{
HandPosForce += RecoilDirection.z * 0.0007f;
IsHandPosDirty = true;
CamRotForce += new Vector2(RecoilDirection.x, -RecoilDirection.y) * CameraRecoilConversionPerc;
IsCamRotDirty = true;
}
private void UpdateReturnBias(float dt)
{
if (HandRotValueCur.x < HandRotValuePrev.x && IsReturning)
{
//skipped the random offset
HandRotValueXAfterRecoil = HandRotValueCur.x;
RecoilOffsetImpulse = Mathf.Abs(HandRotValueCur.x * WeaponRotationForceReturnSpeed) * 0.01f;
//mount?
if (HandRotValueCur.y > 0)
{
LastReturnOffsetSign = 1;
}
else if (HandRotValueCur.y < 0)
{
LastReturnOffsetSign = -1;
}
else
{
LastReturnOffsetSign = Random.Range(-1f, 1f) >= 0f ? 1 : -1;
}
//Log.Out($"Upkick RecoilOffsetImpulse {RecoilOffsetImpulse} Sign {LastReturnOffsetSign} HandRotVelocityY {HandRotVelocity.y} HandRotValueCurY {HandRotValueCur.y}");
return;
}
if (rangedData.state == ItemActionFiringState.Off && IsReturning)
{
RecoilOffsetImpulse *= BiasDamping;
if (RecoilOffsetImpulse <= 0.001f)
{
IsReturning = false;
//RecoilOffsetImpulse = 0f;
//LastReturnOffsetSign = 0;
return;
}
HandRotVelocity.y += RecoilOffsetImpulse * LastReturnOffsetSign;
//Log.Out($"Apply RecoilOffsetImpulse {RecoilOffsetImpulse} Sign {LastReturnOffsetSign} HandRotVelocityY {HandRotVelocity.y} HandRotValueCurY {HandRotValueCur.y}");
}
}
}
}
public static class ProceduralRecoilUpdater
{
public static EntityPlayerLocal player;
public static ActionModuleProceduralRecoil.EFTProceduralRecoilData RecoilData
{
get
{
return (player?.inventory?.holdingItemData?.actionData?[MultiActionManager.GetActionIndexForEntity(player)] as IModuleContainerFor<ActionModuleProceduralRecoil.EFTProceduralRecoilData>)?.Instance;
}
}
private static float SetAimIntensity
{
set
{
if (RecoilData != null)
{
RecoilData.HandRotValueAimIntensity = value;
}
}
}
private static bool SetDontUpdate
{
set
{
ActionModuleProceduralRecoil.EFTProceduralRecoilData.dontUpdateParam = value;
}
}
//====== cam rotation apply
public static Vector3 PreUpdateCamFwd;
public static float CamRecoilLerpSpeed, CamRecoilLerpSpeedStep = 0.1f;
public static Vector2 CamRecoilLerpSpeedRange = new Vector2(0.1f, 0.2f);
public static Vector2 CamRecoilOffsetStable, CamRecoilOffsetCur;
public static Quaternion LocalCamRotOffsetCur, InverseWorldCamShakeOffsetCur, InverseWorldCamKickOffsetCur, InverseWorldCamTotalOffsetCur;
public static float CamAimRecoilPosSmoothIn = 8f, CamAimRecoilPosSmoothOut = 6f;
private static Vector3 CamAimRecoilPosOffsetCur, CamAimRecoilPosOffsetStable/*, CamAimRecoilPosOffsetTarget*/;
public static float LastShotTime, CamAimRecoilPosLerpSpeedXYMin = 7, CamAimRecoilPosLerpSpeedXYMax = 8, CamAimRecoilPosLerpSpeedStep = 5;
public static Vector2 CamAimRecoilRotOffsetCur;
public static float CamAimRecoilRotOffsetLerpSpeed = 15f;
public static void InitPlayer(EntityPlayerLocal player)
{
ProceduralRecoilUpdater.player = player;
PreUpdateCamFwd = player.cameraTransform?.forward ?? Vector3.forward;
CamRecoilLerpSpeed = 0.01f;
CamRecoilOffsetCur = Vector2.zero;
CamRecoilOffsetStable = Vector2.zero;
LocalCamRotOffsetCur = Quaternion.identity;
InverseWorldCamShakeOffsetCur = Quaternion.identity;
InverseWorldCamKickOffsetCur = Quaternion.identity;
InverseWorldCamTotalOffsetCur = Quaternion.identity;
CamAimRecoilPosOffsetCur = Vector3.zero;
CamAimRecoilPosOffsetStable = Vector3.zero;
//CamAimRecoilPosOffsetTarget = Vector3.zero;
LastShotTime = 0;
CamAimRecoilRotOffsetCur = Vector2.zero;
}
//public static void SetTargetRecoilPosOffset(Vector3 offset)
//{
// CamAimRecoilPosOffsetTarget = player.cameraTransform.InverseTransformDirection(offset);
//}
public static Vector3 GetCurRecoilPosOffset()
{
return player.cameraTransform.TransformDirection(CamAimRecoilPosOffsetCur);
}
public static void FixedUpdate(float dt)
{
if (!player)
{
return;
}
ActionModuleProceduralRecoil.EFTProceduralRecoilData recoilData = RecoilData;
if (recoilData != null)
{
recoilData.FixedUpdate(dt);
}
}
public static void LateUpdateCameraRot(float dt)
{
if (!player)
{
return;
}
PreUpdateCamFwd = player.cameraTransform.forward;
ActionModuleProceduralRecoil.EFTProceduralRecoilData recoilData = RecoilData;
if (recoilData != null)
{
var rangedData = recoilData.rangedData;
bool aiming = rangedData.invData.holdingEntity.AimingGun;
if (recoilData.HandRotValueCur != Vector2.zero)
{
//when shooting while aiming, adds a portion of hand rot to camera;
//return to 0 when not aiming or shooting
if (rangedData.state != ItemActionFiringState.Off && aiming)
{
CamRecoilLerpSpeed = Mathf.Clamp(CamRecoilLerpSpeed + CamRecoilLerpSpeedStep * dt, CamRecoilLerpSpeedRange.x, CamRecoilLerpSpeedRange.y);
if (!recoilData.IsStable)
{
CamRecoilOffsetStable = recoilData.HandRotValueCur;
}
CamRecoilOffsetCur = Vector2.Lerp(CamRecoilOffsetCur, CamRecoilOffsetStable, CamRecoilLerpSpeed);
}
else
{
CamRecoilLerpSpeed = Mathf.Clamp(CamRecoilLerpSpeed - CamRecoilLerpSpeedStep * dt, CamRecoilLerpSpeedRange.x, CamRecoilLerpSpeedRange.y);
if (rangedData.state == ItemActionFiringState.Off && aiming)
{
CamRecoilOffsetStable = CamRecoilOffsetCur = Vector2.Lerp(CamRecoilOffsetCur, recoilData.HandRotValueCur, CamRecoilLerpSpeed);
}
else
{
CamRecoilOffsetStable = CamRecoilOffsetCur = Vector2.Lerp(CamRecoilOffsetCur, Vector2.zero, CamRecoilLerpSpeed);
}
}
}
else
{
CamRecoilLerpSpeed = Mathf.Clamp(CamRecoilLerpSpeed - CamRecoilLerpSpeedStep * dt, CamRecoilLerpSpeedRange.x, CamRecoilLerpSpeedRange.y);
CamRecoilOffsetStable = CamRecoilOffsetCur = Vector2.Lerp(CamRecoilOffsetCur, Vector2.zero, CamRecoilLerpSpeed);
}
//if (CamRecoilOffsetCur.sqrMagnitude > 0.0001f)
//{
// Log.Out($"CamRecoilOffsetCur {CamRecoilOffsetCur}, CamRecoilOffsetStable {CamRecoilOffsetStable}, HandRotValueCur {recoilData.HandRotValueCur}");
//}
if (aiming && rangedData.state != ItemActionFiringState.Off)
{
CamAimRecoilRotOffsetCur = Vector2.Lerp(CamAimRecoilRotOffsetCur, -recoilData.HandRotValueApply, CamAimRecoilRotOffsetLerpSpeed * dt);
}
else
{
CamAimRecoilRotOffsetCur = Vector2.Lerp(CamAimRecoilRotOffsetCur, Vector2.zero, 5 * dt);
}
}
else
{
CamRecoilLerpSpeed = Mathf.Clamp(CamRecoilLerpSpeed - CamRecoilLerpSpeedStep * dt, CamRecoilLerpSpeedRange.x, CamRecoilLerpSpeedRange.y);
CamRecoilOffsetStable = CamRecoilOffsetCur = Vector2.Lerp(CamRecoilOffsetCur, Vector2.zero, CamRecoilLerpSpeed);
CamAimRecoilPosOffsetStable = CamAimRecoilPosOffsetCur = Vector3.Lerp(CamAimRecoilPosOffsetCur, Vector3.zero, CamAimRecoilPosSmoothOut * dt);
CamAimRecoilRotOffsetCur = Vector2.Lerp(CamAimRecoilRotOffsetCur, Vector2.zero, 5 * dt);
}
Vector2 realCamRecoilOffset = CamRecoilOffsetCur;
if (recoilData != null)
{
realCamRecoilOffset += recoilData.CamRotValue;
}
//realCamRecoilOffset.x = Mathf.Min(0, realCamRecoilOffset.x);
LocalCamRotOffsetCur = Quaternion.Euler(realCamRecoilOffset);
Quaternion targetCameraRotation = Quaternion.Euler(CamRecoilOffsetCur) * player.cameraTransform.localRotation;
Quaternion cameraParentRotation = player.cameraTransform.parent?.rotation ?? Quaternion.identity;
InverseWorldCamKickOffsetCur = player.cameraTransform.rotation * Quaternion.Inverse(cameraParentRotation * targetCameraRotation);
if (recoilData != null)
{
targetCameraRotation = Quaternion.Euler(recoilData.CamRotValue) * player.cameraTransform.localRotation;
InverseWorldCamShakeOffsetCur = player.cameraTransform.rotation * Quaternion.Inverse(cameraParentRotation * targetCameraRotation);
}
else
{
InverseWorldCamShakeOffsetCur = Quaternion.identity;
}
targetCameraRotation = LocalCamRotOffsetCur * player.cameraTransform.localRotation;
InverseWorldCamTotalOffsetCur = player.cameraTransform.rotation * Quaternion.Inverse(cameraParentRotation * targetCameraRotation);
}
public static void LateUpdateCamRecoilPosOffset(Vector3 CamAimRecoilPosOffsetTarget, float dt, bool aiming)
{
CamAimRecoilPosOffsetTarget = player.cameraTransform.InverseTransformDirection(CamAimRecoilPosOffsetTarget);
if (CamAimRecoilPosOffsetTarget != Vector3.zero && aiming)
{
float lerpStepXY = Mathf.Lerp(CamAimRecoilPosLerpSpeedXYMin, CamAimRecoilPosLerpSpeedXYMax, (Time.time - LastShotTime) * CamAimRecoilPosLerpSpeedStep);
ActionModuleProceduralRecoil.EFTProceduralRecoilData recoilData = RecoilData;
ItemActionRanged.ItemActionDataRanged rangedData = recoilData.rangedData;
if (rangedData.state == ItemActionFiringState.Loop && aiming)
{
if (!recoilData.IsStable)
{
CamAimRecoilPosOffsetStable = CamAimRecoilPosOffsetTarget;
}
CamAimRecoilPosOffsetCur = new Vector3(Mathf.Lerp(CamAimRecoilPosOffsetCur.x, CamAimRecoilPosOffsetTarget.x, lerpStepXY * dt),
Mathf.Lerp(CamAimRecoilPosOffsetCur.y, CamAimRecoilPosOffsetTarget.y, lerpStepXY * dt),
Mathf.Lerp(CamAimRecoilPosOffsetCur.z, CamAimRecoilPosOffsetTarget.z, CamAimRecoilPosSmoothIn * dt));
}
else
{
if (rangedData.state != ItemActionFiringState.Loop && aiming)
{
CamAimRecoilPosOffsetCur = new Vector3(Mathf.Lerp(CamAimRecoilPosOffsetCur.x, CamAimRecoilPosOffsetTarget.x, lerpStepXY * dt),
Mathf.Lerp(CamAimRecoilPosOffsetCur.y, CamAimRecoilPosOffsetTarget.y, lerpStepXY * dt),
Mathf.Lerp(CamAimRecoilPosOffsetCur.z, CamAimRecoilPosOffsetTarget.z, CamAimRecoilPosSmoothIn * dt));
}
else
{
CamAimRecoilPosOffsetCur = Vector3.Lerp(CamAimRecoilPosOffsetCur, Vector3.zero, CamAimRecoilPosSmoothOut * dt);
}
CamAimRecoilPosOffsetStable = CamAimRecoilPosOffsetCur;
}
}
else
{
CamAimRecoilPosOffsetStable = CamAimRecoilPosOffsetCur = Vector3.Lerp(CamAimRecoilPosOffsetCur, Vector3.zero, CamAimRecoilPosSmoothOut * dt);
}
}
public static void LateUpdateApplyCamRot()
{
if (!player || !player.bFirstPersonView)
{
return;
}
player.cameraTransform.localRotation *= LocalCamRotOffsetCur;
player.cameraTransform.localPosition += CamAimRecoilPosOffsetCur;
}
}
[HarmonyPatch]
public static class ProceduralRecoilPatches
{
[HarmonyPatch(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.Awake))]
[HarmonyPostfix]
private static void Postfix_Awake_EntityPlayerLocal(EntityPlayerLocal __instance)
{
ProceduralRecoilUpdater.InitPlayer(__instance);
}
[HarmonyPatch(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.LateUpdate))]
[HarmonyPostfix]
private static void Postfix_LateUpdate_EntityPlayerLocal(EntityPlayerLocal __instance)
{
ProceduralRecoilUpdater.LateUpdateCameraRot(Time.deltaTime);
if (__instance.bFirstPersonView)
{
ProceduralRecoilUpdater.RecoilData?.LateUpdate(Time.deltaTime);
}
}
[HarmonyPatch(typeof(vp_FPCamera), nameof(vp_FPCamera.LateUpdate))]
[HarmonyPostfix]
private static void Postfix_LateUpdate_vp_FPCamera()
{
ProceduralRecoilUpdater.LateUpdateApplyCamRot();
}
[HarmonyPatch(typeof(vp_FPWeapon), nameof(vp_FPWeapon.FixedUpdate))]
[HarmonyPostfix]
private static void Postfix_FixedUpdate_vp_FPWeapon()
{
ProceduralRecoilUpdater.FixedUpdate(Time.fixedDeltaTime);
}
}