2020-05-31 17:07:22 +00:00
using SanAndreasUnity.Behaviours.World ;
using SanAndreasUnity.Importing.Vehicles ;
using SanAndreasUnity.Utilities ;
using System.Collections ;
2019-05-23 20:10:57 +00:00
using System.Collections.Generic ;
2020-05-31 17:07:22 +00:00
using System.Linq ;
using UnityEngine ;
using VehicleDef = SanAndreasUnity . Importing . Items . Definitions . VehicleDef ;
namespace SanAndreasUnity.Behaviours.Vehicles
{
public enum VehicleLight
{
FrontLeft = 1 ,
FrontRight = 2 ,
RearLeft = 4 ,
RearRight = 8 ,
Front = FrontLeft | FrontRight ,
Rear = RearLeft | RearRight ,
All = Front | Rear
}
public enum VehicleBlinkerMode
{
None , Left , Right , Emergency
}
#if CLIENT
public partial class Vehicle : Networking . Networkable
#else
public partial class Vehicle : MonoBehaviour
#endif
{
2019-05-23 20:10:57 +00:00
static List < Vehicle > s_vehicles = new List < Vehicle > ( ) ;
public static IEnumerable < Vehicle > AllVehicles = > s_vehicles ;
2019-05-25 23:51:29 +00:00
public static int NumVehicles = > s_vehicles . Count ;
2021-02-04 21:16:53 +00:00
public static IEnumerable < Rigidbody > AllVehicleRigidBodies = > AllVehicles . Select ( v = > v . RigidBody ) . Where ( r = > r ! = null ) ;
2019-05-23 20:10:57 +00:00
2020-05-31 17:07:22 +00:00
private static int _sLayer = - 1 ;
[HideInInspector]
public Light m_frontLeftLight , m_frontRightLight , m_rearLeftLight , m_rearRightLight ;
2021-11-21 20:52:11 +00:00
private bool frontLeftLightOk = true , frontRightLightOk = true , rearLeftLightOk = true , rearRightLightOk = true ;
2020-05-31 17:07:22 +00:00
private const float blinkerSum = 1.5f ;
private Material directionalLightsMat ;
internal VehicleBlinkerMode blinkerMode ;
public bool m_frontLeftLightOk
{
get
{
return frontLeftLightOk ;
}
set
{
frontLeftLightOk = value ;
SetLight ( VehicleLight . FrontLeft , value ? 1 : 0 ) ;
}
}
public bool m_frontRightLightOk
{
get
{
return frontRightLightOk ;
}
set
{
frontRightLightOk = value ;
SetLight ( VehicleLight . FrontRight , value ? 1 : 0 ) ;
}
}
public bool m_rearLeftLightOk
{
get
{
return rearLeftLightOk ;
}
set
{
rearLeftLightOk = value ;
SetLight ( VehicleLight . RearLeft , value ? 1 : 0 ) ;
}
}
public bool m_rearRightLightOk
{
get
{
return rearRightLightOk ;
}
set
{
rearRightLightOk = value ;
SetLight ( VehicleLight . RearRight , value ? 1 : 0 ) ;
}
}
public static int Layer
{
get { return _sLayer = = - 1 ? _sLayer = UnityEngine . LayerMask . NameToLayer ( "Vehicle" ) : _sLayer ; }
}
public static int LayerMask { get { return 1 < < Layer ; } }
2020-06-02 18:44:08 +00:00
public static int MeshLayer = > UnityEngine . LayerMask . NameToLayer ( "VehicleMesh" ) ;
2022-01-02 19:31:57 +00:00
public static int MeshLayerMask = > 1 < < MeshLayer ;
2020-06-02 18:44:08 +00:00
2021-11-14 22:53:23 +00:00
public static readonly Color32 DefaultVehicleColor = default ;
private readonly Color32 [ ] _colors = { DefaultVehicleColor , DefaultVehicleColor , DefaultVehicleColor , DefaultVehicleColor } ;
public IReadOnlyList < Color32 > Colors = > _colors ;
2021-02-13 21:22:03 +00:00
private readonly float [ ] _lights = { 0 , 0 , 0 , 0 } ;
2020-05-31 17:07:22 +00:00
private MaterialPropertyBlock _props ;
2021-02-13 21:38:35 +00:00
private static readonly int CarColorPropertyId = Shader . PropertyToID ( "_CarColor" ) ;
private static readonly int CarEmissionPropertyId = Shader . PropertyToID ( "_CarEmission" ) ;
2020-05-31 17:07:22 +00:00
private bool _colorsChanged , _isNightToggled ;
2021-11-17 18:05:34 +00:00
public event System . Action onColorsChanged = delegate { } ;
2020-05-31 17:07:22 +00:00
private const float constRearNightIntensity = . 7f ;
public bool IsNightToggled
{
get
{
return _isNightToggled ;
}
set
{
_isNightToggled = value ;
SetLight ( VehicleLight . FrontLeft , _isNightToggled ? VehicleAPI . frontLightIntensity : 0 ) ;
SetLight ( VehicleLight . FrontRight , _isNightToggled ? VehicleAPI . frontLightIntensity : 0 ) ;
SetLight ( VehicleLight . RearLeft , _isNightToggled ? constRearNightIntensity : 0 ) ;
SetLight ( VehicleLight . RearRight , _isNightToggled ? constRearNightIntensity : 0 ) ;
}
}
private VehicleController _controller ;
2019-05-22 23:32:54 +00:00
bool m_isServer = > Net . NetStatus . IsServer ;
public bool IsControlledByLocalPlayer = > Ped . Instance ! = null & & Ped . Instance . CurrentVehicle = = this & & Ped . Instance . CurrentVehicleSeat . IsDriver ;
2020-07-01 21:36:01 +00:00
public Mirror . NetworkIdentity NetIdentity { get ; private set ; }
2019-05-23 23:36:16 +00:00
public Mirror . NetworkTransform NetTransform { get ; private set ; }
2020-05-04 23:39:16 +00:00
List < Ped > m_lastPreparedPeds = new List < Ped > ( ) ;
2020-06-20 15:55:27 +00:00
public string DescriptionForLogging
{
get
{
if ( this . Definition ! = null )
return $"(modelId={this.Definition.Id}, name={this.Definition.GameName})" ;
else
return $"(gameObjectName={this.gameObject.name}, instanceId={this.GetInstanceID()})" ;
}
}
2020-05-31 17:07:22 +00:00
private void Awake ( )
{
2020-07-01 21:36:01 +00:00
this . NetIdentity = this . GetComponentOrThrow < Mirror . NetworkIdentity > ( ) ;
2019-05-23 23:36:16 +00:00
this . NetTransform = this . GetComponent < Mirror . NetworkTransform > ( ) ;
2020-05-31 17:07:22 +00:00
_props = new MaterialPropertyBlock ( ) ;
2020-06-09 17:45:25 +00:00
this . Awake_Damage ( ) ;
2020-05-04 15:56:22 +00:00
this . Awake_Radio ( ) ;
2020-05-31 17:07:22 +00:00
}
2019-05-23 20:10:57 +00:00
void OnEnable ( )
{
s_vehicles . Add ( this ) ;
}
void OnDisable ( )
{
s_vehicles . Remove ( this ) ;
2020-05-07 20:21:05 +00:00
2020-06-20 14:21:29 +00:00
VehiclePhysicsConstants . Changed - = this . UpdateValues ;
2020-05-07 20:21:05 +00:00
this . OnDisable_Radio ( ) ;
2020-06-02 21:56:36 +00:00
2020-06-02 23:10:38 +00:00
if ( this . HighDetailMeshesParent ! = null )
2020-06-02 21:56:36 +00:00
{
2020-06-02 23:10:38 +00:00
Destroy ( this . HighDetailMeshesParent . gameObject ) ;
2020-06-02 21:56:36 +00:00
}
2019-05-23 20:10:57 +00:00
}
2019-05-24 15:09:31 +00:00
void Start ( )
{
this . ApplySyncRate ( VehicleManager . Instance . vehicleSyncRate ) ;
2019-11-08 11:54:45 +00:00
2020-05-04 15:56:22 +00:00
this . Start_Radio ( ) ;
2019-11-08 11:54:45 +00:00
2020-06-20 15:55:27 +00:00
Debug . Log ( $"Created vehicle {this.DescriptionForLogging}, time: {F.CurrentDateForLogging}" ) ;
2019-05-24 15:09:31 +00:00
}
2020-05-31 17:07:22 +00:00
public void SetColors ( params int [ ] clrIndices )
{
2021-11-14 22:53:23 +00:00
this . SetColors ( CarColors . FromIndices ( clrIndices ) ) ;
}
public void SetColors ( Color32 [ ] colors )
{
/ * if ( colors . Length > 4 )
throw new System . ArgumentException ( "Vehicle can not have more than 4 colors" ) ; * /
for ( int i = 0 ; i < 4 & & i < colors . Length ; + + i )
2020-05-31 17:07:22 +00:00
{
2021-11-14 22:53:23 +00:00
if ( _colors [ i ] . Equals ( colors [ i ] ) )
continue ;
_colors [ i ] = colors [ i ] ;
2020-05-31 17:07:22 +00:00
_colorsChanged = true ;
}
}
private Light GetLight ( VehicleLight light )
{
//if (light == VehicleLight.All || light == VehicleLight.Front || light == VehicleLight.Rear) throw new System.Exception("Light must be right or left, can't be general!");
switch ( light )
{
case VehicleLight . FrontLeft :
return m_frontLeftLight ;
case VehicleLight . FrontRight :
return m_frontRightLight ;
case VehicleLight . RearLeft :
return m_rearLeftLight ;
case VehicleLight . RearRight :
return m_rearRightLight ;
}
return null ;
}
private bool IsLightOk ( VehicleLight light )
{
switch ( light )
{
case VehicleLight . FrontLeft :
return m_frontLeftLightOk ;
case VehicleLight . FrontRight :
return m_frontRightLightOk ;
case VehicleLight . RearLeft :
return m_rearLeftLightOk ;
case VehicleLight . RearRight :
return m_rearRightLightOk ;
}
return true ;
}
private bool IsAnyLightPowered ( )
{
if ( _lights ! = null )
return _lights . Any ( x = > x > 0 ) ;
return false ;
}
public void SetLight ( VehicleLight light , float brightness )
{
brightness = Mathf . Clamp01 ( brightness ) ;
for ( var i = 0 ; i < 4 ; + + i )
{
var bit = 1 < < i ;
if ( ( ( int ) light & bit ) = = bit )
{
VehicleLight parsedLight = ( VehicleLight ) bit ; //VehicleAPI.ParseFromBit(i);
if ( IsLightOk ( parsedLight ) )
{
Light lightObj = GetLight ( parsedLight ) ;
bool mustRearPower = _isNightToggled & & ! VehicleAPI . IsFrontLight ( light ) ;
if ( brightness > 0 | | mustRearPower )
{
if ( lightObj ! = null & & ! lightObj . enabled )
{
lightObj . enabled = true ;
lightObj . intensity = mustRearPower ? constRearNightIntensity : brightness ;
}
}
else
{
if ( lightObj ! = null ) lightObj . enabled = false ;
}
SetLight ( i , mustRearPower ? constRearNightIntensity : brightness ) ;
}
}
}
}
private void SetLight ( int index , float brightness )
{
if ( _lights [ index ] = = brightness ) return ;
_lights [ index ] = brightness ;
_colorsChanged = true ;
}
public VehicleDef Definition { get ; private set ; }
public Transform DriverTransform { get ; private set ; }
2019-04-29 18:28:10 +00:00
public bool HasDriverSeat { get { return DriverTransform ! = null & & DriverTransform . childCount > 0 ; } }
2020-05-31 17:07:22 +00:00
public VehicleController StartControlling ( )
{
//SetAllCarLights();
2019-04-29 23:52:19 +00:00
return _controller ? ? ( _controller = gameObject . GetOrAddComponent < VehicleController > ( ) ) ;
2020-05-31 17:07:22 +00:00
}
public void SetAllCarLights ( )
{
// Implemented: Add lights
Transform headlights = this . GetComponentWithName < Transform > ( "headlights" ) ,
taillights = this . GetComponentWithName < Transform > ( "taillights" ) ;
Vehicle vh = gameObject . GetComponent < Vehicle > ( ) ;
if ( headlights ! = null )
{
m_frontLeftLight = VehicleAPI . SetCarLight ( vh , headlights , VehicleLight . FrontLeft ) ;
m_frontRightLight = VehicleAPI . SetCarLight ( vh , headlights , VehicleLight . FrontRight ) ;
}
if ( taillights ! = null )
{
m_rearLeftLight = VehicleAPI . SetCarLight ( vh , taillights , VehicleLight . RearLeft ) ;
m_rearRightLight = VehicleAPI . SetCarLight ( vh , taillights , VehicleLight . RearRight ) ;
}
m_frontLeftLightOk = m_frontLeftLight ! = null ;
m_frontRightLightOk = m_frontRightLight ! = null ;
m_rearLeftLightOk = m_rearLeftLight ! = null ;
m_rearRightLightOk = m_rearRightLight ! = null ;
}
2020-05-03 00:28:25 +00:00
public SeatAlignment GetSeatAlignmentOfClosestSeat ( Vector3 position )
2020-05-31 17:07:22 +00:00
{
2020-05-03 00:28:25 +00:00
var seat = FindClosestSeat ( position ) ;
return seat ! = null ? seat . Alignment : SeatAlignment . None ;
}
public Seat FindClosestSeat ( Vector3 position )
{
if ( this . Seats . Count < 1 )
return null ;
2020-05-31 17:07:22 +00:00
2020-05-03 00:28:25 +00:00
return this . Seats . Aggregate ( ( a , b ) = >
Vector3 . Distance ( position , a . Parent . position ) < Vector3 . Distance ( position , b . Parent . position ) ? a : b ) ;
2020-05-31 17:07:22 +00:00
}
public Transform FindClosestSeatTransform ( Vector3 position )
{
2020-05-03 00:28:25 +00:00
var seat = FindClosestSeat ( position ) ;
return seat ! = null ? seat . Parent : null ;
2020-05-31 17:07:22 +00:00
}
public Seat GetSeat ( SeatAlignment alignment )
{
return _seats . FirstOrDefault ( x = > x . Alignment = = alignment ) ;
}
2019-04-29 18:27:17 +00:00
public Seat DriverSeat = > _seats . FirstOrDefault ( s = > s . IsDriver ) ;
2020-05-31 17:07:22 +00:00
public Transform GetSeatTransform ( SeatAlignment alignment )
{
2020-05-02 23:33:37 +00:00
var seat = GetSeat ( alignment ) ;
return seat ! = null ? seat . Parent : null ;
2020-05-31 17:07:22 +00:00
}
2020-05-04 23:39:16 +00:00
public bool IsLocalPedInside ( )
{
var ped = Ped . Instance ;
if ( ped ! = null )
{
return this . Seats . Exists ( s = > s . OccupyingPed = = ped ) ;
}
return false ;
}
2020-05-31 17:07:22 +00:00
public void StopControlling ( )
{
2019-04-29 23:52:19 +00:00
//Destroy(_controller);
//_controller = null;
2020-05-31 17:07:22 +00:00
}
2020-05-04 23:39:16 +00:00
internal void OnPedPreparedForVehicle ( Ped ped , Seat seat )
{
int numPedsToAdd = this . Seats . Count - m_lastPreparedPeds . Count ;
for ( int i = 0 ; i < numPedsToAdd ; i + + )
{
m_lastPreparedPeds . Add ( null ) ;
}
int index = this . Seats . FindIndex ( s = > s = = seat ) ;
if ( m_lastPreparedPeds [ index ] = = ped )
return ;
m_lastPreparedPeds [ index ] = ped ;
2020-05-07 15:58:47 +00:00
seat . TimeWhenPedChanged = Time . time ;
2020-05-04 23:39:16 +00:00
2020-05-06 21:57:52 +00:00
this . OnPedAssignedToVehicle_Radio ( ped , seat ) ;
2020-05-04 23:39:16 +00:00
}
internal void OnPedRemovedFromVehicle ( Ped ped , Seat seat )
{
int numPedsToAdd = this . Seats . Count - m_lastPreparedPeds . Count ;
for ( int i = 0 ; i < numPedsToAdd ; i + + )
{
m_lastPreparedPeds . Add ( null ) ;
}
int index = this . Seats . FindIndex ( s = > s = = seat ) ;
m_lastPreparedPeds [ index ] = null ;
2020-05-07 15:58:47 +00:00
seat . TimeWhenPedChanged = Time . time ;
2020-05-04 23:39:16 +00:00
}
2021-02-13 21:52:43 +00:00
private void UpdateMaterials ( )
2020-05-31 17:07:22 +00:00
{
2021-11-17 18:05:34 +00:00
if ( ! _colorsChanged )
return ;
2020-05-31 17:07:22 +00:00
_colorsChanged = false ;
2021-02-13 21:52:43 +00:00
UpdateMaterials ( _frames , _colors , _lights , _props ) ;
2021-11-17 18:05:34 +00:00
F . InvokeEventExceptionSafe ( this . onColorsChanged ) ;
2021-02-13 21:52:43 +00:00
}
public static void UpdateMaterials (
FrameContainer frames ,
2021-11-14 22:53:23 +00:00
Color32 [ ] paintJobColors ,
2021-02-13 21:52:43 +00:00
float [ ] lights ,
MaterialPropertyBlock materialPropertyBlock )
{
2021-02-13 21:18:27 +00:00
Color32 headLightColor = new Color32 ( 255 , 255 , 255 , 255 ) ;
Color32 tailLightColor = new Color32 ( 255 , 255 , 255 , 255 ) ;
// compute car colors
Color32 [ ] carColors = new [ ]
{
new Color32 ( 255 , 255 , 255 , 255 ) ,
2021-11-14 22:53:23 +00:00
paintJobColors [ 0 ] ,
paintJobColors [ 1 ] ,
paintJobColors [ 2 ] ,
paintJobColors [ 3 ] ,
2021-02-13 21:18:27 +00:00
headLightColor ,
headLightColor ,
tailLightColor ,
tailLightColor ,
} ;
// compute car emissions
float [ ] carEmissions = new [ ]
{
0f ,
0f ,
0f ,
0f ,
0f ,
2021-02-13 21:52:43 +00:00
Mathf . Exp ( lights [ 0 ] * 2 ) - 1 ,
Mathf . Exp ( lights [ 1 ] * 2 ) - 1 ,
Mathf . Exp ( lights [ 2 ] * 2 ) - 1 ,
Mathf . Exp ( lights [ 3 ] * 2 ) - 1 ,
2021-02-13 21:18:27 +00:00
} ;
2020-05-31 17:07:22 +00:00
2021-02-13 21:52:43 +00:00
foreach ( var frame in frames )
2020-05-31 17:07:22 +00:00
{
var mr = frame . GetComponent < MeshRenderer > ( ) ;
if ( mr = = null ) continue ;
2021-02-13 21:18:27 +00:00
// get color index from each material, and assign properties accordingly
var materials = mr . sharedMaterials ;
for ( int i = 0 ; i < materials . Length ; i + + )
{
2021-02-13 21:38:35 +00:00
int carColorIndex = materials [ i ] . GetInt ( Importing . Conversion . Geometry . CarColorIndexId ) ;
2021-02-13 21:52:43 +00:00
materialPropertyBlock . SetColor ( CarColorPropertyId , carColors [ carColorIndex ] ) ;
materialPropertyBlock . SetFloat ( CarEmissionPropertyId , carEmissions [ carColorIndex ] ) ;
mr . SetPropertyBlock ( materialPropertyBlock , i ) ;
2021-02-13 21:18:27 +00:00
}
2020-05-31 17:07:22 +00:00
}
}
private void Update ( )
{
2021-02-05 00:08:07 +00:00
if ( Net . NetStatus . IsServer
| | ( this . IsControlledByLocalPlayer & & VehicleManager . Instance . controlWheelsOnLocalPlayer & & ! VehicleManager . Instance . destroyWheelCollidersOnClient ) )
2020-05-31 17:07:22 +00:00
{
2021-02-05 00:08:07 +00:00
foreach ( var wheel in _wheels )
{
Vector3 position = Vector3 . zero ;
2020-05-31 17:07:22 +00:00
2021-02-05 00:08:07 +00:00
WheelHit wheelHit ;
2020-05-31 17:07:22 +00:00
2021-02-05 00:08:07 +00:00
if ( wheel . Collider . GetGroundHit ( out wheelHit ) )
{
position . y = ( wheelHit . point . y - wheel . Collider . transform . position . y ) + wheel . Collider . radius ;
}
else
{
position . y - = wheel . Collider . suspensionDistance ;
}
2020-05-31 17:07:22 +00:00
2021-02-05 00:08:07 +00:00
wheel . Child . transform . localPosition = position ;
2020-05-31 17:07:22 +00:00
2021-02-05 00:08:07 +00:00
UpdateWheelRotation ( wheel , wheel . Collider . rpm , wheel . Collider . steerAngle ) ;
}
2020-05-31 17:07:22 +00:00
}
2021-11-17 18:05:34 +00:00
this . UpdateMaterials ( ) ;
2019-11-08 11:54:45 +00:00
2020-06-09 17:45:25 +00:00
this . Update_Damage ( ) ;
2020-05-04 15:56:22 +00:00
this . Update_Radio ( ) ;
2020-06-02 20:40:46 +00:00
this . UpdateHighDetailMeshes ( ) ;
2021-07-18 04:03:43 +00:00
if ( Net . NetStatus . IsServer & & this . transform . position . y < - 2000f )
{
Object . Destroy ( this . gameObject ) ;
}
2020-05-31 17:07:22 +00:00
}
private void FixedUpdate ( )
{
// NetworkingFixedUpdate();
PhysicsFixedUpdate ( ) ;
}
2021-02-04 22:17:12 +00:00
public static void UpdateWheelRotation ( Wheel wheel , float rpm , float steerAngle )
{
// reset the yaw
wheel . Child . localRotation = wheel . Roll ;
// calculate new roll
wheel . Child . Rotate ( wheel . IsLeftHand ? Vector3 . left : Vector3 . right , rpm / 60.0f * 360.0f * Time . deltaTime ) ;
wheel . Roll = wheel . Child . localRotation ;
// apply yaw
wheel . Child . localRotation = Quaternion . AngleAxis ( steerAngle , Vector3 . up ) * wheel . Roll ;
}
2020-06-02 20:40:46 +00:00
void UpdateHighDetailMeshes ( )
{
2020-06-02 23:10:38 +00:00
this . HighDetailMeshesParent . SetPositionAndRotation ( this . transform . position , this . transform . rotation ) ;
2020-06-02 20:40:46 +00:00
for ( int i = 0 ; i < m_highDetailMeshObjectsToUpdate . Count ; i + + )
{
var item = m_highDetailMeshObjectsToUpdate [ i ] ;
item . Value . SetPositionAndRotation ( item . Key . position , item . Key . rotation ) ;
}
}
2020-05-31 17:07:22 +00:00
private IEnumerator DelayedBlinkersTurnOff ( )
{
yield return new WaitForSeconds ( blinkerSum ) ;
if ( blinkerMode ! = VehicleBlinkerMode . None )
blinkerMode = VehicleBlinkerMode . None ;
}
2019-05-24 15:09:31 +00:00
public void ApplySyncRate ( float syncRate )
{
foreach ( var comp in this . GetComponents < Mirror . NetworkBehaviour > ( ) )
comp . syncInterval = 1.0f / syncRate ;
// also assign it to NetworkTransform, because it may be disabled
if ( this . NetTransform ! = null )
this . NetTransform . syncInterval = 1.0f / syncRate ;
}
2020-05-31 17:07:22 +00:00
}
}