using NWH.Common.SceneManagement;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Serialization;
namespace NWH.Common.Vehicles
{
///
/// Base class for all NWH vehicles.
///
[DisallowMultipleComponent]
[RequireComponent(typeof(Rigidbody))]
public abstract class Vehicle : MonoBehaviour
{
// Points to the last enabled vehicle.
public static Vehicle ActiveVehicle
{
get
{
int activeVehicleCount = ActiveVehicles.Count;
if (activeVehicleCount == 0)
{
return null;
}
else
{
return ActiveVehicles[activeVehicleCount - 1];
}
}
}
public static List ActiveVehicles = new List();
///
/// Called when active vehicle is changed.
/// First parameter is the previously active vehicle and the second parameter is the currently active vehicle
/// (at the time of the callback). Params can be null.
///
public static UnityEvent onActiveVehicleChanged = new UnityEvent();
///
/// Cached value of vehicle rigidbody.
///
[UnityEngine.Tooltip(" Cached value of vehicle rigidbody.")]
[NonSerialized]
public Rigidbody vehicleRigidbody;
///
/// Cached value of vehicle transform.
///
[Tooltip(" Cached value of vehicle transform.")]
[NonSerialized]
public Transform vehicleTransform;
///
/// True if the vehicle can be driven by the player. False if the
/// vehicle is passive (such as a trailer). A vehicle that has isPlayerControllable
/// can be the ActiveVehicle.
///
public bool isPlayerControllable = true;
#region CAMERA
///
/// True when camera is inside vehicle (cockpit, cabin, etc.).
/// Set by the 'CameraInsideVehicle' component.
/// Used for audio effects.
///
public bool CameraInsideVehicle
{
get { return _cameraInsideVehicle; }
set
{
if (_cameraInsideVehicle && !value)
{
onCameraExitVehicle.Invoke();
}
else if (!CameraInsideVehicle && value)
{
onCameraEnterVehicle.Invoke();
}
_cameraInsideVehicle = value;
}
}
private bool _cameraInsideVehicle = false;
///
/// Called when the camera enters the vehicle.
///
public UnityEvent onCameraEnterVehicle = new UnityEvent();
///
/// Called when the camera exists the vehicle.
///
public UnityEvent onCameraExitVehicle = new UnityEvent();
#endregion
#region EVENTS
///
/// Called when vehicle is put to sleep.
///
[Tooltip(" Called when vehicle is put to sleep.")]
[NonSerialized]
public UnityEvent onDisable = new UnityEvent();
///
/// Called when vehicle is woken up.
///
[Tooltip(" Called when vehicle is woken up.")]
[NonSerialized]
public UnityEvent onEnable = new UnityEvent();
#endregion
#region MULTIPLAYER
///
/// Determines if vehicle is running locally is synchronized over active multiplayer framework.
///
[Tooltip(" Determines if vehicle is running locally is synchronized over active multiplayer framework.")]
private bool _multiplayerIsRemote = false;
///
/// True if the vehicle is a client (remote).
/// If true the input is expected to be synced through the network.
///
public bool MultiplayerIsRemote
{
get
{
return _multiplayerIsRemote;
}
set
{
if (_multiplayerIsRemote && !value)
{
onMultiplayerStatusChanged.Invoke(false);
}
else if (!_multiplayerIsRemote && value)
{
onMultiplayerStatusChanged.Invoke(true);
}
_multiplayerIsRemote = value;
}
}
///
/// Invoked when MultiplayerIsRemote value gets changed.
/// Is true if remote.
///
public UnityEvent onMultiplayerStatusChanged = new UnityEvent();
#endregion
#region PHYSICAL_PROPERTIES
///
/// Cached acceleration in local coordinates (z-forward)
///
public Vector3 LocalAcceleration { get; private set; }
///
/// Cached acceleration in forward direction in local coordinates (z-forward).
///
public float LocalForwardAcceleration { get; private set; }
///
/// Cached velocity in forward direction in local coordinates (z-forward).
///
public float LocalForwardVelocity { get; private set; }
///
/// Cached velocity in m/s in local coordinates.
///
public Vector3 LocalVelocity { get; private set; }
///
/// Cached speed of the vehicle in the forward direction. ALWAYS POSITIVE.
/// For positive/negative version use SpeedSigned.
///
public float Speed
{
get { return LocalForwardVelocity < 0 ? -LocalForwardVelocity : LocalForwardVelocity; }
}
///
/// Cached speed of the vehicle in the forward direction. Can be positive (forward) or negative (reverse).
/// Equal to LocalForwardVelocity.
///
public float SpeedSigned
{
get { return LocalForwardVelocity; }
}
///
/// Cached velocity of the vehicle in world coordinates.
///
public Vector3 Velocity { get; protected set; }
///
/// Cached velocity magnitude of the vehicle in world coordinates.
///
public float VelocityMagnitude { get; protected set; }
///
/// Cached angular velocity of the vehicle.
///
public Vector3 AngularVelocity { get; protected set; }
///
/// Cached angular velocity maginitude of the vehicle.
///
public float AngularVelocityMagnitude { get; protected set; }
private Vector3 _prevLocalVelocity;
#endregion
public virtual void Awake()
{
vehicleTransform = transform;
vehicleRigidbody = GetComponent();
vehicleRigidbody.interpolation = RigidbodyInterpolation.Interpolate;
}
public virtual void FixedUpdate()
{
// Pre-calculate values
_prevLocalVelocity = LocalVelocity;
Velocity = vehicleRigidbody.velocity;
LocalVelocity = transform.InverseTransformDirection(Velocity);
LocalAcceleration = (LocalVelocity - _prevLocalVelocity) / Time.fixedDeltaTime;
LocalForwardVelocity = LocalVelocity.z;
LocalForwardAcceleration = LocalAcceleration.z;
VelocityMagnitude = Velocity.magnitude;
AngularVelocity = vehicleRigidbody.angularVelocity;
AngularVelocityMagnitude = AngularVelocity.magnitude;
}
public virtual void OnEnable()
{
onEnable.Invoke();
if (isPlayerControllable)
{
if (!ActiveVehicles.Contains(this))
{
Vehicle previouslyActiveVehicle = ActiveVehicle;
ActiveVehicles.Add(this);
Vehicle currentlyActiveVehicle = ActiveVehicle;
onActiveVehicleChanged.Invoke(previouslyActiveVehicle, currentlyActiveVehicle);
}
}
}
public virtual void OnDisable()
{
onDisable.Invoke();
if (isPlayerControllable)
{
Vehicle previouslyActiveVehicle = ActiveVehicle;
ActiveVehicles.RemoveAll(vehicle => vehicle == this);
Vehicle currentlyActiveVehicle = ActiveVehicle;
onActiveVehicleChanged.Invoke(previouslyActiveVehicle, currentlyActiveVehicle);
}
}
}
}