2021-09-19 18:09:11 +00:00
|
|
|
using SanAndreasUnity.Behaviours.Peds.AI;
|
2020-05-31 17:07:22 +00:00
|
|
|
using UnityEngine;
|
|
|
|
using SanAndreasUnity.Utilities;
|
|
|
|
using SanAndreasUnity.Importing.Weapons;
|
|
|
|
using SanAndreasUnity.Importing.Animation;
|
|
|
|
using SanAndreasUnity.Behaviours.Weapons;
|
|
|
|
|
|
|
|
namespace SanAndreasUnity.Behaviours.Peds.States
|
|
|
|
{
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Base class for all aim-movement states.
|
|
|
|
/// </summary>
|
|
|
|
public abstract class BaseAimMovementState : BaseScriptState, IAimState
|
|
|
|
{
|
|
|
|
protected Weapon m_weapon { get { return m_ped.CurrentWeapon; } }
|
|
|
|
|
|
|
|
public abstract AnimId aimWithArm_LowerAnim { get; }
|
|
|
|
|
|
|
|
public virtual float AimAnimMaxTime { get { return m_weapon.AimAnimMaxTime; } }
|
|
|
|
public virtual float AimAnimFireMaxTime { get { return m_weapon.AimAnimFireMaxTime; } }
|
|
|
|
|
2021-12-26 19:55:58 +00:00
|
|
|
protected bool m_wasAimingBackWithAWAWeapon = false;
|
|
|
|
protected float m_timeSinceAimingBackWithAWAWeapon = 0f;
|
2020-05-31 17:07:22 +00:00
|
|
|
|
2021-12-26 22:16:14 +00:00
|
|
|
public virtual float TimeUntilStateCanBeSwitchedToOtherAimMovementState => PedManager.Instance.timeUntilAimMovementStateCanBeSwitchedToOtherAimMovementState;
|
|
|
|
public virtual float TimeUntilStateCanBeEnteredFromOtherAimMovementState => PedManager.Instance.timeUntilAimMovementStateCanBeEnteredFromOtherAimMovementState;
|
2020-05-31 17:07:22 +00:00
|
|
|
|
2022-01-01 22:42:38 +00:00
|
|
|
protected float m_timeWhenDidUnderAimDetection = 0f;
|
|
|
|
|
2021-12-26 19:55:58 +00:00
|
|
|
|
2021-12-26 22:16:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
public override void OnBecameActive()
|
2021-12-26 19:55:58 +00:00
|
|
|
{
|
|
|
|
base.OnBecameActive();
|
|
|
|
|
|
|
|
m_wasAimingBackWithAWAWeapon = false;
|
|
|
|
m_timeSinceAimingBackWithAWAWeapon = 0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void UpdateState()
|
2020-05-31 17:07:22 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
base.UpdateState ();
|
|
|
|
|
|
|
|
if (!this.IsActiveState)
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
2019-07-07 15:43:57 +00:00
|
|
|
if (m_isServer)
|
|
|
|
{
|
|
|
|
if (this.SwitchToNonAimMovementState ())
|
|
|
|
return;
|
|
|
|
if (this.SwitchToFiringState ())
|
|
|
|
return;
|
|
|
|
if (this.SwitchToOtherAimMovementState ())
|
|
|
|
return;
|
|
|
|
if (this.SwitchToFallingState ())
|
|
|
|
return;
|
|
|
|
}
|
2020-05-31 17:07:22 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void LateUpdateState ()
|
|
|
|
{
|
|
|
|
base.LateUpdateState ();
|
|
|
|
|
|
|
|
if (!this.IsActiveState)
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_ped.WeaponHolder.UpdateWeaponTransform ();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected virtual bool SwitchToNonAimMovementState ()
|
|
|
|
{
|
|
|
|
// check if we should exit aiming state
|
2020-02-11 14:42:13 +00:00
|
|
|
|
2021-12-18 16:16:40 +00:00
|
|
|
|
|
|
|
if (null == m_weapon)
|
|
|
|
{
|
|
|
|
BaseMovementState.SwitchToMovementStateBasedOnInput(m_ped);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-12-26 23:39:59 +00:00
|
|
|
// prevent fast state switching
|
|
|
|
if (!EnoughTimePassedToSwitchToNonAimState(m_ped, Mathf.Max(this.AimAnimMaxTime, PedManager.Instance.minTimeToReturnToNonAimStateFromAimState)))
|
2021-12-18 16:16:40 +00:00
|
|
|
return false;
|
2021-12-26 23:39:59 +00:00
|
|
|
|
2021-12-18 16:16:40 +00:00
|
|
|
if (!m_ped.IsAimOn)
|
2020-05-31 17:07:22 +00:00
|
|
|
{
|
|
|
|
BaseMovementState.SwitchToMovementStateBasedOnInput (m_ped);
|
|
|
|
return true;
|
|
|
|
}
|
2020-02-11 14:42:13 +00:00
|
|
|
|
|
|
|
if (m_ped.IsSprintOn)
|
|
|
|
{
|
2021-12-18 16:16:40 +00:00
|
|
|
if (m_weapon.CanSprintWithIt)
|
2020-02-11 14:42:13 +00:00
|
|
|
{
|
|
|
|
m_ped.SwitchState<SprintState>();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-31 17:07:22 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-12-26 23:39:59 +00:00
|
|
|
public static bool EnoughTimePassedToSwitchToNonAimState(Ped ped, float timeRequiredToPass)
|
|
|
|
{
|
|
|
|
var states = ped.CachedNonAimStates;
|
|
|
|
|
|
|
|
for (int i = 0; i < states.Count; i++)
|
|
|
|
{
|
|
|
|
if (states[i].TimeSinceDeactivated < timeRequiredToPass)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-05-31 17:07:22 +00:00
|
|
|
protected virtual bool SwitchToFiringState ()
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected virtual bool SwitchToOtherAimMovementState ()
|
|
|
|
{
|
2021-12-26 22:16:14 +00:00
|
|
|
System.Type type = GetAimMovementStateToSwitchToBasedOnInput(m_ped);
|
|
|
|
var state = (BaseAimMovementState)m_ped.GetStateOrLogError(type);
|
|
|
|
|
|
|
|
if (!EnoughTimePassedToSwitchBetweenAimMovementStates(this, state))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
m_ped.SwitchState(type);
|
|
|
|
|
2020-05-31 17:07:22 +00:00
|
|
|
return ! this.IsActiveState;
|
|
|
|
}
|
|
|
|
|
2021-12-26 22:16:14 +00:00
|
|
|
public static bool EnoughTimePassedToSwitchBetweenAimMovementStates(
|
|
|
|
BaseAimMovementState currentState,
|
|
|
|
BaseAimMovementState targetState)
|
|
|
|
{
|
|
|
|
if (currentState.TimeSinceActivated < currentState.TimeUntilStateCanBeSwitchedToOtherAimMovementState)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (targetState.TimeSinceDeactivated < targetState.TimeUntilStateCanBeEnteredFromOtherAimMovementState)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-05-31 17:07:22 +00:00
|
|
|
protected virtual bool SwitchToFallingState ()
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void SwitchToAimMovementStateBasedOnInput (Ped ped)
|
|
|
|
{
|
2021-12-26 20:50:31 +00:00
|
|
|
System.Type type = GetAimMovementStateToSwitchToBasedOnInput(ped);
|
|
|
|
ped.SwitchState(type);
|
|
|
|
}
|
2020-05-31 17:07:22 +00:00
|
|
|
|
2021-12-26 20:50:31 +00:00
|
|
|
public static System.Type GetAimMovementStateToSwitchToBasedOnInput(Ped ped)
|
|
|
|
{
|
2020-05-31 17:07:22 +00:00
|
|
|
if (ped.IsWalkOn)
|
|
|
|
{
|
2021-12-26 20:50:31 +00:00
|
|
|
return typeof(WalkAimState);
|
2020-05-31 17:07:22 +00:00
|
|
|
}
|
2020-02-11 14:42:13 +00:00
|
|
|
else if (ped.IsRunOn || ped.IsSprintOn)
|
2020-05-31 17:07:22 +00:00
|
|
|
{
|
2021-12-26 20:50:31 +00:00
|
|
|
if (ped.CurrentWeapon != null && ped.CurrentWeapon.HasFlag(GunFlag.AIMWITHARM))
|
|
|
|
return typeof(RunAimState);
|
2020-05-31 17:07:22 +00:00
|
|
|
else
|
2021-12-26 20:50:31 +00:00
|
|
|
return typeof(WalkAimState);
|
2020-05-31 17:07:22 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-12-26 20:50:31 +00:00
|
|
|
return typeof(StandAimState);
|
2020-05-31 17:07:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected virtual void RotateSpine()
|
|
|
|
{
|
|
|
|
BaseAimMovementState.RotateSpineToMatchAimDirection (m_ped);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void RotateSpineToMatchAimDirection (Ped ped)
|
|
|
|
{
|
|
|
|
if (null == ped.CurrentWeapon)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ped.CurrentWeapon.HasFlag (GunFlag.AIMWITHARM))
|
|
|
|
return;
|
|
|
|
|
2019-05-22 23:20:40 +00:00
|
|
|
ped.PlayerModel.Spine.forward = ped.AimDirection;
|
2020-05-31 17:07:22 +00:00
|
|
|
|
|
|
|
// now apply offset to spine rotation
|
|
|
|
// this has to be done because spine is not rotated properly - is it intentionally done, or is it error in model importing ?
|
|
|
|
|
2019-05-22 23:20:40 +00:00
|
|
|
// TODO: actually, spine direction should be the same as ped's direction, not aim direction
|
|
|
|
|
2020-05-31 17:07:22 +00:00
|
|
|
Vector3 eulers = ped.WeaponHolder.SpineOffset;
|
|
|
|
if (ped.CurrentWeapon.HasFlag (GunFlag.AIMWITHARM))
|
|
|
|
eulers.y = 0;
|
|
|
|
ped.PlayerModel.Spine.Rotate (eulers);
|
|
|
|
// PlayerModel.ChangeSpineRotation (this.CurrentWeaponTransform.forward, Camera.transform.position + Camera.transform.forward * Camera.farClipPlane - this.CurrentWeaponTransform.position, SpineRotationSpeed, ref tempSpineLocalEulerAngles, ref targetRot, ref spineRotationLastFrame);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
protected virtual void RotatePedInDirectionOfAiming()
|
|
|
|
{
|
|
|
|
if (m_ped.CurrentWeapon.HasFlag (GunFlag.AIMWITHARM))
|
|
|
|
return;
|
|
|
|
|
|
|
|
BaseAimMovementState.RotatePedInDirectionOfAiming( m_ped );
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void RotatePedInDirectionOfAiming(Ped ped)
|
|
|
|
{
|
|
|
|
|
|
|
|
// Vector3 lookAtPos = Camera.transform.position + Camera.transform.forward * 500;
|
|
|
|
// lookAtPos.y = m_player.transform.position.y;
|
|
|
|
//
|
|
|
|
// m_player.transform.LookAt (lookAtPos, Vector3.up);
|
|
|
|
|
2019-07-07 16:05:04 +00:00
|
|
|
Vector3 forward = ped.AimDirection;
|
2020-05-31 17:07:22 +00:00
|
|
|
forward.y = 0;
|
|
|
|
forward.Normalize ();
|
|
|
|
// m_player.transform.forward = forward;
|
|
|
|
ped.Heading = forward;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual void StartFiring()
|
|
|
|
{
|
|
|
|
BaseFireMovementState.SwitchToFireMovementStateBasedOnInput(m_ped);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void UpdateAnims ()
|
|
|
|
{
|
|
|
|
if (m_weapon != null)
|
|
|
|
{
|
|
|
|
AnimationState state;
|
|
|
|
|
|
|
|
if (m_weapon.HasFlag (GunFlag.AIMWITHARM))
|
|
|
|
state = this.UpdateAnimsAWA ();
|
|
|
|
else
|
|
|
|
state = this.UpdateAnimsNonAWA ();
|
|
|
|
|
|
|
|
if (m_weapon)
|
|
|
|
{
|
|
|
|
m_weapon.AimAnimState = state;
|
|
|
|
if (state)
|
2022-01-01 21:28:32 +00:00
|
|
|
{
|
|
|
|
this.UpdateAimAnim (state);
|
|
|
|
|
2022-01-01 21:42:31 +00:00
|
|
|
// do this right after UpdateAimAnim(), because that's the state when weapon conducts attack
|
2022-01-01 22:42:38 +00:00
|
|
|
if (Time.time - m_timeWhenDidUnderAimDetection >= PedManager.Instance.timeIntervalToUpdateUnderAimStatus)
|
|
|
|
{
|
|
|
|
m_timeWhenDidUnderAimDetection = Time.time;
|
|
|
|
UpdateUnderAimDetection(m_ped);
|
|
|
|
}
|
2022-01-01 21:28:32 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-31 17:07:22 +00:00
|
|
|
|
|
|
|
if (m_weapon && m_weapon.HasFlag(GunFlag.AIMWITHARM))
|
|
|
|
{
|
|
|
|
// update arm transforms
|
|
|
|
// this has to be done after updating aim anim
|
|
|
|
this.UpdateArmTransformsForAWA();
|
|
|
|
}
|
|
|
|
|
|
|
|
// spine should be rotated no matter if state was changed or not during anim updating
|
|
|
|
// this should be done AFTER updating anims
|
|
|
|
this.RotateSpine ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Update anims for non AWA (AIMWITHARM) weapons.
|
|
|
|
/// </summary>
|
|
|
|
protected virtual AnimationState UpdateAnimsNonAWA()
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Update anims for AWA (AIMWITHARM) weapons.
|
|
|
|
/// </summary>
|
|
|
|
protected virtual AnimationState UpdateAnimsAWA()
|
|
|
|
{
|
|
|
|
return BaseAimMovementState.UpdateAnimsAWA (m_ped, this.aimWithArm_LowerAnim);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static AnimationState UpdateAnimsAWA(Ped ped, AnimId aimWithArm_LowerAnim)
|
|
|
|
{
|
|
|
|
|
|
|
|
// aim with arm
|
|
|
|
// eg: pistol, tec9, sawnoff
|
|
|
|
|
|
|
|
var model = ped.PlayerModel;
|
|
|
|
|
|
|
|
model.Play2Anims (new AnimId (AnimGroup.Colt45, AnimIndex.colt45_fire), aimWithArm_LowerAnim);
|
|
|
|
|
|
|
|
var state = model.LastAnimState;
|
|
|
|
model.LastAnimState.wrapMode = WrapMode.ClampForever;
|
|
|
|
|
|
|
|
model.RemoveAllMixingTransforms (model.LastAnimState);
|
|
|
|
model.AddMixingTransform (model.LastAnimState, model.RightClavicle, true);
|
|
|
|
|
|
|
|
model.AddMixingTransform (model.LastSecondaryAnimState, model.LeftClavicle, true);
|
|
|
|
model.AddMixingTransforms (model.LastSecondaryAnimState, model.Pelvis, model.Belly, model.Spine, model.UpperSpine, model.RBreast, model.LBreast, model.Neck);
|
|
|
|
|
|
|
|
if (model.AnimsChanged) {
|
|
|
|
// reset model state
|
|
|
|
model.ResetModelState ();
|
|
|
|
// sample the animation, because otherwise, model will remain in original state for 1 frame
|
|
|
|
model.AnimComponent.Sample ();
|
|
|
|
}
|
|
|
|
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected virtual void UpdateAimAnim(AnimationState state)
|
|
|
|
{
|
|
|
|
BaseAimMovementState.UpdateAimAnim (m_ped, state, this.AimAnimMaxTime, this.AimAnimFireMaxTime, () => this.TryFire());
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void UpdateAimAnim(Ped ped, AnimationState state, float aimAnimMaxTime, float aimAnimFireMaxTime,
|
|
|
|
System.Func<bool> tryFireFunc)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (state.time > aimAnimMaxTime) {
|
|
|
|
|
|
|
|
if (ped.IsFiring) {
|
|
|
|
state.enabled = true;
|
|
|
|
|
|
|
|
// check if anim reached end
|
|
|
|
if(state.time >= aimAnimFireMaxTime) {
|
|
|
|
// anim reached end, revert it to start
|
|
|
|
|
|
|
|
state.time = aimAnimMaxTime;
|
|
|
|
ped.AnimComponent.Sample ();
|
|
|
|
|
|
|
|
// if (!ped.IsFireOn || !ped.IsAimOn)
|
|
|
|
{
|
|
|
|
// no longer firing
|
2019-07-07 16:37:33 +00:00
|
|
|
if (Net.NetStatus.IsServer)
|
|
|
|
{
|
|
|
|
ped.StopFiring ();
|
|
|
|
}
|
2020-05-31 17:07:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// check if we should start firing
|
|
|
|
|
|
|
|
if (ped.IsFireOn && tryFireFunc()) {
|
|
|
|
// we started firing
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// we should remain in aim state
|
|
|
|
state.time = aimAnimMaxTime;
|
|
|
|
ped.AnimComponent.Sample ();
|
|
|
|
state.enabled = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-01-01 21:42:31 +00:00
|
|
|
public static void UpdateUnderAimDetection(Ped ped)
|
|
|
|
{
|
|
|
|
GetEffectiveFirePosAndDir(ped, WeaponAttackParams.Default, out Vector3 pos, out Vector3 dir);
|
|
|
|
Damageable damagable = ped.CurrentWeapon.ProjectileRaycastForDamagable(
|
|
|
|
pos, dir, WeaponAttackParams.Default, out bool attackWillBeConducted, out DamageInfo damageInfo);
|
|
|
|
if (attackWillBeConducted && damagable != null)
|
|
|
|
{
|
|
|
|
var targetPed = damagable.GetComponent<Ped>();
|
|
|
|
if (targetPed != null)
|
|
|
|
{
|
|
|
|
targetPed.OnUnderAimOfOtherPed(damageInfo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-31 17:07:22 +00:00
|
|
|
protected virtual void UpdateArmTransformsForAWA()
|
|
|
|
{
|
2021-12-26 19:55:58 +00:00
|
|
|
BaseAimMovementState.UpdateArmTransformsForAWA (
|
|
|
|
m_ped, ref m_wasAimingBackWithAWAWeapon, ref m_timeSinceAimingBackWithAWAWeapon);
|
2020-05-31 17:07:22 +00:00
|
|
|
}
|
|
|
|
|
2021-12-26 19:55:58 +00:00
|
|
|
public static void UpdateArmTransformsForAWA(Ped ped, ref bool wasAimingBack, ref float timeSinceAimingBack)
|
2020-05-31 17:07:22 +00:00
|
|
|
{
|
|
|
|
var player = ped;
|
|
|
|
var model = ped.PlayerModel;
|
|
|
|
var weapon = ped.CurrentWeapon;
|
|
|
|
|
|
|
|
|
|
|
|
float timePerc = Mathf.Clamp01 (model.LastAnimState.time / weapon.AimAnimMaxTime);
|
|
|
|
|
|
|
|
// rotate arm to match direction of player
|
|
|
|
|
|
|
|
// we'll need a few adjustments, because arm's right vector is player's forward vector,
|
|
|
|
// and arm's forward vector is player's down vector => arm's up is player's left
|
|
|
|
// Vector3 forward = - player.transform.right ; // -player.transform.up;
|
|
|
|
// Vector3 up = player.transform.up; // -player.transform.right;
|
|
|
|
// Vector3 lookAtPos = player.transform.position + forward * 500;
|
|
|
|
|
|
|
|
model.ResetFrameState (model.RightUpperArm);
|
|
|
|
model.ResetFrameState (model.RightForeArm);
|
|
|
|
model.ResetFrameState (model.RightHand);
|
|
|
|
|
2019-05-22 22:40:07 +00:00
|
|
|
Vector3 aimDir = player.AimDirection;
|
2020-05-31 17:07:22 +00:00
|
|
|
Vector3 aimDirLocal = player.transform.InverseTransformDirection (aimDir);
|
|
|
|
|
|
|
|
bool isAimingOnOppositeSide = aimDirLocal.x < 0f;
|
|
|
|
float oppositeSideAngle = Vector3.Angle( Vector3.forward, aimDirLocal.WithXAndZ () );
|
2021-12-26 19:55:58 +00:00
|
|
|
|
|
|
|
bool isAimingBack = oppositeSideAngle > WeaponsManager.Instance.AIMWITHARM_maxAimAngle
|
|
|
|
|| (wasAimingBack && timeSinceAimingBack < WeaponsManager.Instance.AIMWITHARM_timeUntilAbleToStopAimingBack);
|
|
|
|
|
|
|
|
if (isAimingBack)
|
|
|
|
timeSinceAimingBack += Time.deltaTime;
|
|
|
|
else
|
|
|
|
timeSinceAimingBack = 0f;
|
|
|
|
|
|
|
|
wasAimingBack = isAimingBack;
|
2020-05-31 17:07:22 +00:00
|
|
|
|
|
|
|
Quaternion startRot = Quaternion.LookRotation( -player.transform.up, player.transform.forward ); // Quaternion.Euler (WeaponsManager.Instance.AIMWITHARM_upperArmStartRotationEulers);
|
|
|
|
Quaternion endRot = Quaternion.LookRotation (aimDir); //Quaternion.LookRotation( forward, up );
|
|
|
|
// Vector3 endForwardLocal = new Vector3(0.9222f, -0.3429f, 0.179f);
|
|
|
|
// Vector3 endUpLocal = new Vector3(-0.3522f, -0.9357f, 0.02171f);
|
|
|
|
Quaternion endRotForeArm = endRot;
|
|
|
|
|
|
|
|
if (isAimingBack) {
|
|
|
|
// aim in the air
|
|
|
|
|
|
|
|
endRot = Quaternion.LookRotation ( Vector3.Lerp(-player.transform.up, player.transform.forward, 0.7f).normalized );
|
|
|
|
|
|
|
|
// we need to apply rotation that is opposite of given offset for x axis - to assure that forehand's up matches ped's back
|
|
|
|
Quaternion q = Quaternion.AngleAxis( 90f - WeaponsManager.Instance.AIMWITHARM_foreArmRotationOffset.x, player.transform.up );
|
|
|
|
endRotForeArm = Quaternion.LookRotation (q * player.transform.up, q * (-player.transform.forward));
|
|
|
|
|
|
|
|
} else if (isAimingOnOppositeSide) {
|
|
|
|
// upper arm will slightly follow direction of aiming, but only along y and z axes
|
|
|
|
// forearm will have direction of aiming
|
|
|
|
|
|
|
|
Vector3 dir = aimDirLocal;
|
|
|
|
dir.x = 0; // no looking left or right
|
|
|
|
if (oppositeSideAngle != 0) {
|
|
|
|
// dir.y = Mathf.Sign (dir.y) * ( Mathf.Abs (dir.y) - 1.0f * oppositeSideAngle / 90f );
|
|
|
|
dir.y -= 1.0f * oppositeSideAngle / 90f;
|
|
|
|
dir.z += 1.0f * oppositeSideAngle / 90f;
|
|
|
|
if (dir.y > 0)
|
|
|
|
dir.y /= (1.0f + oppositeSideAngle / 90f);
|
|
|
|
// if (Mathf.Abs(dir.y) > dir.z)
|
|
|
|
// dir.z = Mathf.Abs(dir.y);
|
|
|
|
}
|
|
|
|
dir.Normalize ();
|
|
|
|
dir = player.transform.TransformDirection (dir);
|
|
|
|
endRot = Quaternion.LookRotation (dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
// lerp
|
|
|
|
Quaternion rot = Quaternion.Lerp( startRot, endRot, timePerc );
|
|
|
|
Quaternion rotForeArm = Quaternion.Lerp (startRot, endRotForeArm, timePerc);
|
|
|
|
|
|
|
|
if (timePerc == 1.0f) {
|
|
|
|
// Vector3 localForward = player.transform.InverseTransformDirection (model.RightUpperArm.forward);
|
|
|
|
// Vector3 localUp = player.transform.InverseTransformDirection (model.RightUpperArm.up);
|
|
|
|
// Debug.LogFormat ("local forward {0}, local up {1}", localForward.ToString("G4"), localUp.ToString("G4"));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Quaternion deltaRot = Quaternion.FromToRotation (Vector3.forward, aimDirLocal);
|
|
|
|
|
|
|
|
// Quaternion worldRot = player.transform.TransformRotation( rot );
|
|
|
|
|
|
|
|
// worldRot *= deltaRot;
|
|
|
|
|
|
|
|
// assign new rotation
|
|
|
|
// 'rot' is in player space
|
|
|
|
// model.RightUpperArm.rotation = worldRot;
|
|
|
|
|
|
|
|
// Quaternion convertRot = Quaternion.Euler (WeaponsManager.Instance.AIMWITHARM_upperArmEndRotationEulers);
|
|
|
|
|
|
|
|
// head rotation
|
|
|
|
Vector3 clampedAimDir = F.ClampDirection (aimDir, player.transform.forward, WeaponsManager.Instance.AIMWITHARM_maxHeadRotationAngle);
|
|
|
|
Quaternion headRot = isAimingBack ? player.transform.rotation : Quaternion.LookRotation (clampedAimDir);
|
|
|
|
// headRot = Quaternion.Lerp( model.Head.rotation, headRot, 0.3f);
|
|
|
|
|
|
|
|
|
|
|
|
// set new rotations and apply aim rotation offsets
|
|
|
|
|
|
|
|
model.Head.rotation = headRot;
|
|
|
|
model.Head.Rotate (WeaponsManager.Instance.AIMWITHARM_headRotationOffset);
|
|
|
|
|
|
|
|
model.RightClavicle.Rotate (WeaponsManager.Instance.AIMWITHARM_clavicleRotationOffset);
|
|
|
|
|
|
|
|
model.RightUpperArm.rotation = rot;
|
|
|
|
model.RightUpperArm.Rotate (WeaponsManager.Instance.AIMWITHARM_upperArmRotationOffset);
|
|
|
|
|
|
|
|
model.RightForeArm.rotation = rotForeArm;
|
|
|
|
model.RightForeArm.Rotate (WeaponsManager.Instance.AIMWITHARM_foreArmRotationOffset);
|
|
|
|
|
|
|
|
model.RightHand.localRotation = Quaternion.identity;
|
|
|
|
model.RightHand.Rotate (WeaponsManager.Instance.AIMWITHARM_handRotationOffset);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-01-01 16:37:48 +00:00
|
|
|
public static void GetEffectiveFirePosAndDir(Ped ped, WeaponAttackParams weaponAttackParams, out Vector3 pos, out Vector3 dir)
|
|
|
|
{
|
|
|
|
if (ped.IsControlledByLocalPlayer || null == ped.PlayerOwner)
|
|
|
|
{
|
|
|
|
pos = ped.FirePosition;
|
|
|
|
dir = ped.FireDirection;
|
|
|
|
}
|
|
|
|
else // this ped is owned by remote client
|
|
|
|
{
|
|
|
|
pos = ped.NetFirePos;
|
|
|
|
dir = ped.NetFireDir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-28 19:31:45 +00:00
|
|
|
public static bool TryFire (Ped ped, WeaponAttackParams weaponAttackParams)
|
2019-07-12 16:41:38 +00:00
|
|
|
{
|
2020-03-22 18:25:24 +00:00
|
|
|
if (ped.CurrentWeapon != null)
|
2019-07-12 22:25:57 +00:00
|
|
|
{
|
2020-03-22 18:25:24 +00:00
|
|
|
if (Net.NetStatus.IsServer)
|
2019-07-12 22:25:57 +00:00
|
|
|
{
|
2022-01-01 16:37:48 +00:00
|
|
|
GetEffectiveFirePosAndDir(ped, weaponAttackParams, out Vector3 pos, out Vector3 dir);
|
|
|
|
return TryFire(ped, pos, dir, weaponAttackParams);
|
2019-07-12 22:25:57 +00:00
|
|
|
}
|
|
|
|
}
|
2019-07-12 16:41:38 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-03-22 18:25:24 +00:00
|
|
|
protected virtual bool TryFire()
|
|
|
|
{
|
2020-03-28 19:31:45 +00:00
|
|
|
return TryFire(m_ped, WeaponAttackParams.Default);
|
2020-03-22 18:25:24 +00:00
|
|
|
}
|
|
|
|
|
2020-03-28 19:31:45 +00:00
|
|
|
public static bool TryFire (Ped ped, Vector3 firePos, Vector3 fireDir, WeaponAttackParams weaponAttackParams)
|
2020-05-31 17:07:22 +00:00
|
|
|
{
|
2020-03-22 15:00:08 +00:00
|
|
|
bool isServer = Net.NetStatus.IsServer;
|
2020-05-31 17:07:22 +00:00
|
|
|
var weapon = ped.CurrentWeapon;
|
|
|
|
|
|
|
|
|
2020-03-22 15:00:08 +00:00
|
|
|
if (!isServer) // for now, do this only on server
|
2019-07-12 20:17:22 +00:00
|
|
|
return false;
|
|
|
|
|
2019-07-12 22:25:57 +00:00
|
|
|
if (Net.NetStatus.IsClientOnly && ! ped.IsControlledByLocalPlayer)
|
|
|
|
return false;
|
|
|
|
|
2019-07-12 16:41:38 +00:00
|
|
|
if (null == weapon)
|
2019-07-07 19:51:30 +00:00
|
|
|
return false;
|
|
|
|
|
2019-07-12 16:41:38 +00:00
|
|
|
if (ped.IsFiring) // already firing
|
2020-05-31 17:07:22 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// check if there is ammo in clip
|
|
|
|
if (weapon.AmmoInClip < 1)
|
|
|
|
return false;
|
|
|
|
|
2020-03-22 15:00:08 +00:00
|
|
|
if (isServer)
|
2019-07-12 16:41:38 +00:00
|
|
|
{
|
|
|
|
ped.StartFiring ();
|
2020-05-31 17:07:22 +00:00
|
|
|
|
2019-07-12 16:41:38 +00:00
|
|
|
if (!ped.IsFiring) // failed to start firing
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
2020-05-31 17:07:22 +00:00
|
|
|
|
|
|
|
// reduce ammo
|
|
|
|
weapon.AmmoInClip --;
|
|
|
|
|
|
|
|
// update gun flash
|
|
|
|
// this.EnableOrDisableGunFlash (ped);
|
|
|
|
if (weapon.GunFlash != null)
|
|
|
|
weapon.GunFlash.gameObject.SetActive (true);
|
|
|
|
weapon.UpdateGunFlashRotation ();
|
|
|
|
|
|
|
|
// fire projectile
|
2020-03-22 15:00:08 +00:00
|
|
|
if (isServer)
|
2020-03-28 19:31:45 +00:00
|
|
|
F.RunExceptionSafe( () => weapon.FireProjectile (firePos, fireDir, weaponAttackParams) );
|
2019-07-12 16:41:38 +00:00
|
|
|
|
|
|
|
// send fire event to server
|
2019-07-12 22:25:57 +00:00
|
|
|
if (Net.NetStatus.IsClientOnly && ped.IsControlledByLocalPlayer)
|
2019-07-12 16:41:38 +00:00
|
|
|
{
|
2019-07-12 20:17:22 +00:00
|
|
|
|
2019-07-12 16:41:38 +00:00
|
|
|
}
|
2020-05-31 17:07:22 +00:00
|
|
|
|
2019-07-08 14:17:55 +00:00
|
|
|
// notify clients
|
2020-03-22 15:00:08 +00:00
|
|
|
if (isServer)
|
|
|
|
Net.PedSync.OnWeaponFired(ped, weapon, firePos);
|
2019-07-08 14:17:55 +00:00
|
|
|
|
2020-05-31 17:07:22 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-03-29 23:46:02 +00:00
|
|
|
public virtual Vector3 GetFirePosition()
|
|
|
|
{
|
|
|
|
return GetFirePosition(m_ped);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Vector3 GetFirePosition(Ped ped)
|
|
|
|
{
|
|
|
|
return ped.CurrentWeapon != null ? ped.CurrentWeapon.GetFirePosWithoutPed() : ped.transform.position;
|
|
|
|
}
|
|
|
|
|
|
|
|
public virtual Vector3 GetFireDirection()
|
|
|
|
{
|
|
|
|
return GetFireDirection(m_ped, () => IsAimingBack(m_ped), WeaponAttackParams.Default);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Vector3 GetFireDirection(Ped ped, System.Func<bool> isAimingBackFunc, WeaponAttackParams weaponAttackParams)
|
|
|
|
{
|
|
|
|
if (null == ped.CurrentWeapon)
|
|
|
|
return ped.AimDirection;
|
|
|
|
|
|
|
|
if (isAimingBackFunc())
|
|
|
|
return ped.transform.up;
|
|
|
|
|
|
|
|
if (ped.IsControlledByLocalPlayer && ped.Camera != null)
|
|
|
|
{
|
|
|
|
// find ray going into the world
|
|
|
|
Ray ray = ped.Camera.GetRayFromCenter();
|
|
|
|
|
|
|
|
// raycast
|
|
|
|
RaycastHit hit;
|
|
|
|
if (ped.CurrentWeapon.ProjectileRaycast(ray.origin, ray.direction, out hit, weaponAttackParams))
|
|
|
|
{
|
|
|
|
return (hit.point - ped.FirePosition).normalized;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if any object is hit, direction will be from fire position to hit point
|
|
|
|
|
|
|
|
// if not, direction will be same as aim direction
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return ped.AimDirection;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static bool IsAimingBack(Ped ped)
|
|
|
|
{
|
|
|
|
if (null == ped.CurrentWeapon)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!ped.CurrentWeapon.HasFlag(GunFlag.AIMWITHARM))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!ped.IsAiming)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Vector3 aimDirLocal = ped.transform.InverseTransformDirection(ped.AimDirection);
|
|
|
|
|
|
|
|
float oppositeSideAngle = Vector3.Angle(Vector3.forward, aimDirLocal.WithXAndZ());
|
|
|
|
return oppositeSideAngle > WeaponsManager.Instance.AIMWITHARM_maxAimAngle;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public virtual void OnClientTriedToFire(Vector3 firePos, Vector3 fireDir)
|
2019-07-12 16:41:38 +00:00
|
|
|
{
|
|
|
|
if (null == m_weapon)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (m_weapon.AmmoInClip < 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!m_ped.IsFiring)
|
|
|
|
m_ped.StartFiring();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-05-31 17:07:22 +00:00
|
|
|
|
|
|
|
public override void RotateCamera ()
|
|
|
|
{
|
|
|
|
base.RotateCamera ();
|
|
|
|
|
|
|
|
// this must be called from here (right after the camera transform is changed), otherwise camera will shake
|
2019-04-28 18:06:44 +00:00
|
|
|
// must check if weapon is null here, because camera is now updated in LateUpdate(), and weapon can become null between Update() and LateUpdate()
|
|
|
|
if (m_weapon)
|
|
|
|
this.RotatePedInDirectionOfAiming ();
|
2020-05-31 17:07:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public override void UpdateCameraZoom()
|
|
|
|
{
|
|
|
|
// ignore
|
|
|
|
}
|
|
|
|
|
2020-03-25 00:16:51 +00:00
|
|
|
public override void CheckCameraCollision()
|
|
|
|
{
|
|
|
|
CheckCameraCollision(m_ped, this.GetCameraFocusPos());
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void CheckCameraCollision (Ped ped, Vector3 cameraFocusPos)
|
2020-05-31 17:07:22 +00:00
|
|
|
{
|
|
|
|
Vector3 castFrom = cameraFocusPos;
|
|
|
|
float distance;
|
2020-03-25 00:16:51 +00:00
|
|
|
Vector3 castDir = -ped.Camera.transform.forward;
|
|
|
|
|
2020-05-31 17:07:22 +00:00
|
|
|
// use distance from gun aiming offset ?
|
2020-03-25 00:16:51 +00:00
|
|
|
if (ped.CurrentWeapon != null && ped.CurrentWeapon.GunAimingOffset != null)
|
2020-05-31 17:07:22 +00:00
|
|
|
{
|
|
|
|
// Vector3 desiredCameraPos = this.transform.TransformPoint (- _player.CurrentWeapon.GunAimingOffset.Aim) + Vector3.up * .5f;
|
|
|
|
// Vector3 desiredCameraPos = this.transform.TransformPoint( new Vector3(0.8f, 1.0f, -1) );
|
2020-03-25 00:16:51 +00:00
|
|
|
Vector3 desiredCameraPos = cameraFocusPos + ped.Camera.transform.TransformVector (ped.WeaponHolder.cameraAimOffset);
|
2020-05-31 17:07:22 +00:00
|
|
|
Vector3 diff = desiredCameraPos - castFrom;
|
|
|
|
distance = diff.magnitude;
|
|
|
|
castDir = diff.normalized;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-03-25 00:16:51 +00:00
|
|
|
distance = ped.CameraDistance;
|
2020-05-31 17:07:22 +00:00
|
|
|
}
|
|
|
|
|
2020-03-25 00:16:51 +00:00
|
|
|
BaseScriptState.CheckCameraCollision(ped, castFrom, castDir, distance);
|
2020-05-31 17:07:22 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void OnSubmitPressed()
|
|
|
|
{
|
|
|
|
|
|
|
|
// try to enter vehicle
|
2019-04-30 21:29:21 +00:00
|
|
|
if (m_isServer)
|
|
|
|
m_ped.TryEnterVehicleInRange ();
|
|
|
|
else
|
|
|
|
base.OnSubmitPressed();
|
2020-05-31 17:07:22 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void OnJumpPressed()
|
|
|
|
{
|
|
|
|
// ignore
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-02-11 17:15:49 +00:00
|
|
|
protected override void OnButtonPressedOnServer(string buttonName)
|
|
|
|
{
|
|
|
|
if (buttonName == "G")
|
|
|
|
{
|
2020-06-05 21:49:50 +00:00
|
|
|
// try to recruit peds to follow you
|
2020-06-05 21:22:30 +00:00
|
|
|
RecruitPedUnderWeaponToFollowPed(m_ped);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void RecruitPedUnderWeaponToFollowPed(Ped pedToFollow)
|
|
|
|
{
|
2020-06-05 21:49:50 +00:00
|
|
|
if (null == pedToFollow.CurrentWeapon)
|
|
|
|
return;
|
|
|
|
|
|
|
|
RaycastHit hit;
|
|
|
|
if (!pedToFollow.CurrentWeapon.ProjectileRaycast(
|
|
|
|
pedToFollow.IsControlledByLocalPlayer ? pedToFollow.FirePosition : pedToFollow.NetFirePos,
|
|
|
|
pedToFollow.IsControlledByLocalPlayer ? pedToFollow.FireDirection : pedToFollow.NetFireDir,
|
|
|
|
out hit,
|
|
|
|
WeaponAttackParams.Default))
|
2020-06-05 21:22:30 +00:00
|
|
|
{
|
2020-06-05 21:49:50 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// see if ped is hit
|
|
|
|
|
|
|
|
var damageable = hit.collider.gameObject.GetComponentInParent<Damageable>();
|
|
|
|
if (null == damageable)
|
|
|
|
return;
|
|
|
|
|
2021-09-11 16:04:50 +00:00
|
|
|
var hitPedAI = damageable.GetComponent<PedAI>();
|
|
|
|
if (hitPedAI != null && hitPedAI.MyPed != pedToFollow)
|
2020-06-05 21:49:50 +00:00
|
|
|
{
|
2021-09-11 16:04:50 +00:00
|
|
|
// ray hit NPC ped
|
|
|
|
hitPedAI.Recruit(pedToFollow);
|
2020-02-11 17:15:49 +00:00
|
|
|
}
|
2020-06-05 21:49:50 +00:00
|
|
|
|
2020-02-11 17:15:49 +00:00
|
|
|
}
|
|
|
|
|
2020-05-31 17:07:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|