SanAndreasUnity/Assets/Scripts/Behaviours/Ped/States/DriveByState.cs

254 lines
8.5 KiB
C#

using UnityEngine;
using SanAndreasUnity.Behaviours.Vehicles;
using System.Collections.Generic;
namespace SanAndreasUnity.Behaviours.Peds.States
{
public class DriveByState : VehicleSittingState, IAimState
{
// TODO:
// - add real aim anims ?
// - drive-by exiting state - activated when going from drive-by to sitting state, or when trying to exit vehicle
// - weapon's gun flash should depend on last time when fired, not on anim time - maybe don't change it, because we may play real aim anims
readonly List<GameObject> m_gameObjectToIgnoreWhenRaycasting = new List<GameObject>();
readonly List<int> m_layersToIgnoreWhenRaycasting = new List<int>();
public WeaponAttackParams WeaponAttackParams
{
get
{
m_gameObjectToIgnoreWhenRaycasting.Clear();
m_layersToIgnoreWhenRaycasting.Clear();
if (this.CurrentVehicle != null)
{
m_gameObjectToIgnoreWhenRaycasting.Add(this.CurrentVehicle.gameObject);
m_layersToIgnoreWhenRaycasting.Add(LayerMask.NameToLayer(Ped.PedBoneLayerName));
if (this.CurrentVehicle.HighDetailMeshesParent != null)
{
m_gameObjectToIgnoreWhenRaycasting.Add(this.CurrentVehicle.HighDetailMeshesParent.gameObject);
m_layersToIgnoreWhenRaycasting.Add(Vehicle.MeshLayer);
}
return new WeaponAttackParams
{
GameObjectsToIgnoreWhenRaycasting = m_gameObjectToIgnoreWhenRaycasting,
LayersToIgnoreWhenRaycasting = m_layersToIgnoreWhenRaycasting,
};
}
return WeaponAttackParams.Default;
}
}
protected override void EnterVehicleInternal()
{
m_vehicleParentOffset = Vector3.zero;
m_model.VehicleParentOffset = Vector3.zero;
BaseVehicleState.PreparePedForVehicle(m_ped, this.CurrentVehicle, this.CurrentVehicleSeat);
// only update firing if ped is not currently firing, because otherwise it can cause stack overflow
// by switching states indefinitely
// also, we must update firing here, because otherwise shooting rate will be lower
this.UpdateAnimsInternal(!m_ped.IsFiring);
}
public bool CanEnterState(Vehicle vehicle, Vehicle.SeatAlignment seatAlignment)
{
var w = m_ped.CurrentWeapon;
return null != w && w.IsGun;
}
public override void UpdateState()
{
// exit drive-by state if ped doesn't have gun weapon
if (m_isServer)
{
var w = m_ped.CurrentWeapon;
if (null == w || !w.IsGun)
{
m_ped.GetStateOrLogError<VehicleSittingState>().EnterVehicle(this.CurrentVehicle, this.CurrentVehicleSeatAlignment);
return;
}
}
base.UpdateState();
}
protected override void UpdateAnimsInternal()
{
this.UpdateAnimsInternal(true);
}
void UpdateAnimsInternal(bool bUpdateFiring)
{
if (this.CurrentVehicleSeat != null)
{
var animId = new Importing.Animation.AnimId("drivebys", this.GetAnimBasedOnAimDir());
m_model.PlayAnim(animId);
m_model.LastAnimState.wrapMode = WrapMode.ClampForever;
if (bUpdateFiring)
{
if (m_ped.CurrentWeapon != null)
{
m_ped.CurrentWeapon.AimAnimState = m_model.LastAnimState;
this.UpdateAimAnim(() => BaseAimMovementState.TryFire(m_ped, this.WeaponAttackParams));
}
}
m_model.VehicleParentOffset = m_model.GetAnim(animId.AnimName).RootEnd;
m_model.RootFrame.transform.localPosition = Vector3.zero;
}
}
string GetAnimBasedOnAimDir()
{
// 4 types: forward, backward, same side, opposite side
Vector3 aimDir = m_ped.AimDirection;
Vector3 vehicleDir = this.CurrentVehicle.transform.forward;
bool isLeftSeat = this.CurrentVehicleSeat.IsLeftHand;
string leftOrRightLetter = isLeftSeat ? "L" : "R";
float angle = Vector3.Angle(aimDir, vehicleDir);
float rightAngle = Vector3.Angle(aimDir, this.CurrentVehicle.transform.right);
if (angle < 45)
{
// aiming forward
return "Gang_Driveby" + leftOrRightLetter + "HS_Fwd";
}
else if (angle < 135)
{
// aiming to left or right side
bool isAimingToLeftSide = rightAngle > 90;
if (isLeftSeat != isAimingToLeftSide) // aiming to opposite side
{
return "Gang_DrivebyTop_" + leftOrRightLetter + "HS";
}
else // aiming to same side
{
return "Gang_Driveby" + leftOrRightLetter + "HS";
}
}
else
{
// aiming backward
return "Gang_Driveby" + leftOrRightLetter + "HS_Bwd";
}
}
void UpdateAimAnim(System.Func<bool> tryFireFunc)
{
var ped = m_ped;
var weapon = ped.CurrentWeapon;
var state = m_model.LastAnimState;
float aimAnimMaxTime = state.length * 0.5f;
if (state.time >= aimAnimMaxTime)
{
// keep the anim at max time
state.time = aimAnimMaxTime;
ped.AnimComponent.Sample();
state.enabled = false;
if (ped.IsFiring)
{
// check if weapon finished firing
if (weapon != null && weapon.TimeSinceFired >= (weapon.AimAnimFireMaxTime - weapon.AimAnimMaxTime))
{
if (Net.NetStatus.IsServer)
{
ped.StopFiring();
}
}
}
else
{
// check if we should start firing
if (ped.IsFireOn && tryFireFunc())
{
// we started firing
}
else
{
// we should remain in aim state
}
}
}
}
public virtual void StartFiring()
{
// switch to firing state
m_ped.GetStateOrLogError<DriveByFireState>().EnterVehicle(this.CurrentVehicle, this.CurrentVehicleSeatAlignment);
}
public override void UpdateCameraZoom()
{
// ignore
}
public override void CheckCameraCollision()
{
BaseScriptState.CheckCameraCollision(m_ped, this.GetCameraFocusPos(), - m_ped.Camera.transform.forward, this.GetCameraDistance());
}
public override Vector3 GetCameraFocusPos()
{
var seat = m_ped.CurrentVehicleSeat;
if (seat != null && seat.Parent != null)
return seat.Parent.transform.position + Vector3.up * DriveByManager.Instance.cameraHeightOffset;
else
return base.GetCameraFocusPos();
//return m_ped.PlayerModel.Head.transform.position;
}
public override float GetCameraDistance()
{
return DriveByManager.Instance.cameraBackwardOffset;
}
public override void OnAimButtonPressed()
{
// switch to sitting state
if (m_isServer)
m_ped.GetStateOrLogError<VehicleSittingState>().EnterVehicle(this.CurrentVehicle, this.CurrentVehicleSeatAlignment);
else
base.OnAimButtonPressed();
}
public virtual Vector3 GetFirePosition()
{
return BaseAimMovementState.GetFirePosition(m_ped);
}
public virtual Vector3 GetFireDirection()
{
return BaseAimMovementState.GetFireDirection(m_ped, () => false, this.WeaponAttackParams);
}
}
}