482 lines
16 KiB
C#
Raw Normal View History

2022-07-20 17:50:01 +08:00
using GTA;
2022-05-22 15:55:26 +08:00
using GTA.Math;
2022-07-20 17:50:01 +08:00
using GTA.Native;
2022-05-22 15:55:26 +08:00
using RageCoop.Core;
2022-07-20 17:50:01 +08:00
using System;
using System.Collections.Generic;
2022-07-17 19:19:23 +08:00
using System.Diagnostics;
2022-07-20 17:50:01 +08:00
using System.Linq;
2022-05-22 15:55:26 +08:00
namespace RageCoop.Client
{
2022-07-01 13:54:18 +08:00
/// <summary>
/// A synchronized vehicle instance
/// </summary>
public class SyncedVehicle : SyncedEntity
2022-05-22 15:55:26 +08:00
{
#region -- CONSTRUCTORS --
/// <summary>
/// Create a local entity (outgoing sync)
/// </summary>
2022-07-01 13:54:18 +08:00
/// <param name="v"></param>
internal SyncedVehicle(Vehicle v)
2022-05-22 15:55:26 +08:00
{
ID=EntityPool.RequestNewID();
2022-05-22 15:55:26 +08:00
MainVehicle=v;
MainVehicle.CanPretendOccupants=false;
OwnerID=Main.LocalPlayerID;
2022-05-22 15:55:26 +08:00
}
/// <summary>
/// Create an empty VehicleEntity
/// </summary>
internal SyncedVehicle()
2022-05-22 15:55:26 +08:00
{
}
internal SyncedVehicle(int id)
2022-05-22 15:55:26 +08:00
{
ID=id;
LastSynced=Main.Ticked;
}
#endregion
/// <summary>
/// VehicleSeat,ID
/// </summary>
2022-07-20 17:50:01 +08:00
public Vehicle MainVehicle { get; internal set; }
public Stopwatch LastSyncedStopWatch = new Stopwatch();
2022-05-22 15:55:26 +08:00
#region LAST STATE
2022-05-22 15:55:26 +08:00
private byte[] _lastVehicleColors = new byte[] { 0, 0 };
private Dictionary<int, int> _lastVehicleMods = new Dictionary<int, int>();
2022-07-30 12:39:09 +08:00
private bool _lastTouchingPlayer = false;
2022-05-22 15:55:26 +08:00
#endregion
#region -- CRITICAL STUFF --
internal Vector3 RotationVelocity { get; set; }
internal float SteeringAngle { get; set; }
internal float ThrottlePower { get; set; }
internal float BrakePower { get; set; }
internal float DeluxoWingRatio { get; set; } = -1;
2022-06-17 15:26:27 +08:00
internal bool IsFlipped
{
get
{
2022-07-30 12:39:09 +08:00
return _isMotorcycle||(Quaternion*Vector3.RelativeTop).Z <(Quaternion*Vector3.RelativeBottom).Z;
2022-06-17 15:26:27 +08:00
}
}
2022-07-30 12:39:09 +08:00
private bool _isMotorcycle;
2022-05-22 15:55:26 +08:00
#endregion
2022-07-17 12:22:11 +08:00
#region FLAGS
internal bool EngineRunning { get { return Flags.HasVehFlag(VehicleDataFlags.IsEngineRunning); } }
private bool _lastTransformed = false;
internal bool Transformed { get { return Flags.HasVehFlag(VehicleDataFlags.IsTransformed); } }
private bool _lastHornActive = false;
internal bool HornActive { get { return Flags.HasVehFlag(VehicleDataFlags.IsHornActive); } }
internal bool LightsOn { get { return Flags.HasVehFlag(VehicleDataFlags.AreLightsOn); } }
internal bool BrakeLightsOn { get { return Flags.HasVehFlag(VehicleDataFlags.AreBrakeLightsOn); } }
internal bool HighBeamsOn { get { return Flags.HasVehFlag(VehicleDataFlags.AreHighBeamsOn); } }
internal bool SireneActive { get { return Flags.HasVehFlag(VehicleDataFlags.IsSirenActive); } }
internal bool IsDead { get { return Flags.HasVehFlag(VehicleDataFlags.IsDead); } }
internal bool IsDeluxoHovering { get { return Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering); } }
#endregion
2022-05-22 15:55:26 +08:00
#region -- VEHICLE STATE --
internal VehicleDataFlags Flags { get; set; }
2022-07-17 12:22:11 +08:00
internal byte LandingGear { get; set; }
2022-06-19 13:36:23 +08:00
internal VehicleRoofState RoofState { get; set; }
internal VehicleDamageModel DamageModel { get; set; }
internal byte[] Colors { get; set; }
internal Dictionary<int, int> Mods { get; set; }
internal float EngineHealth { get; set; }
2022-07-20 17:50:01 +08:00
internal VehicleLockStatus LockStatus { get; set; }
2022-05-22 15:55:26 +08:00
/// <summary>
/// VehicleSeat,PedID
/// </summary>
internal Dictionary<VehicleSeat, SyncedPed> Passengers { get; set; }
internal byte RadioStation = 255;
2022-06-20 10:27:45 +08:00
internal string LicensePlate { get; set; }
2022-07-09 22:18:00 +08:00
internal int _lastLivery = -1;
internal int Livery { get; set; } = -1;
2022-07-09 19:59:17 +08:00
internal bool _checkSeat { get; set; } = true;
2022-05-22 15:55:26 +08:00
#endregion
internal override void Update()
2022-05-22 15:55:26 +08:00
{
2022-07-20 17:50:01 +08:00
2022-05-22 15:55:26 +08:00
#region -- INITIAL CHECK --
// Check if all data avalible
2022-07-20 17:50:01 +08:00
if (!IsReady) { return; }
2022-05-22 15:55:26 +08:00
#endregion
#region -- CHECK EXISTENCE --
if ((MainVehicle == null) || (!MainVehicle.Exists()) || (MainVehicle.Model.Hash != Model))
2022-05-22 15:55:26 +08:00
{
if (!CreateVehicle())
{
return;
}
2022-05-22 15:55:26 +08:00
}
2022-07-17 19:19:23 +08:00
// Skip update if no new sync message has arrived.
2022-07-21 09:53:07 +08:00
if (!NeedUpdate) {
return;
}
2022-05-22 15:55:26 +08:00
#endregion
#region -- SYNC CRITICAL --
2022-07-30 12:39:09 +08:00
2022-05-22 15:55:26 +08:00
if (SteeringAngle != MainVehicle.SteeringAngle)
{
MainVehicle.CustomSteeringAngle((float)(Math.PI / 180) * SteeringAngle);
}
MainVehicle.ThrottlePower=ThrottlePower;
MainVehicle.BrakePower=BrakePower;
2022-07-30 12:39:09 +08:00
var v = Main.P.CurrentVehicle;
if (v!= null && MainVehicle.IsTouching(v))
{
DisplayVehicle(00.1f);
}
else
{
DisplayVehicle(1);
}
2022-07-17 12:22:11 +08:00
#region FLAGS
if (IsDead)
{
if (MainVehicle.IsDead)
{
return;
}
else
{
MainVehicle.Explode();
}
}
else
{
if (MainVehicle.IsDead)
{
2022-07-21 22:42:29 +08:00
Main.Delay(() =>
{
if (MainVehicle.IsDead && !IsDead)
{
MainVehicle.Repair();
}
},1000);
2022-07-17 12:22:11 +08:00
}
}
2022-07-17 20:12:25 +08:00
if (MainVehicle.IsOnFire)
{
if (!Flags.HasVehFlag(VehicleDataFlags.IsOnFire))
{
Function.Call(Hash.STOP_ENTITY_FIRE, MainVehicle);
}
}
else if (Flags.HasVehFlag(VehicleDataFlags.IsOnFire))
{
Function.Call(Hash.START_ENTITY_FIRE, MainVehicle);
}
2022-07-17 12:22:11 +08:00
if (EngineRunning != MainVehicle.IsEngineRunning)
{
MainVehicle.IsEngineRunning = EngineRunning;
}
if (LightsOn != MainVehicle.AreLightsOn)
{
MainVehicle.AreLightsOn = LightsOn;
}
if (HighBeamsOn != MainVehicle.AreHighBeamsOn)
{
MainVehicle.AreHighBeamsOn = HighBeamsOn;
}
if (MainVehicle.IsSubmarineCar)
{
if (Transformed)
{
if (!_lastTransformed)
{
_lastTransformed = true;
Function.Call(Hash._TRANSFORM_VEHICLE_TO_SUBMARINE, MainVehicle.Handle, false);
}
}
else if (_lastTransformed)
{
_lastTransformed = false;
Function.Call(Hash._TRANSFORM_SUBMARINE_TO_VEHICLE, MainVehicle.Handle, false);
}
}
if (MainVehicle.IsAircraft)
{
if (LandingGear != (byte)MainVehicle.LandingGearState)
{
MainVehicle.LandingGearState = (VehicleLandingGearState)LandingGear;
}
}
else
{
if (MainVehicle.HasSiren && SireneActive != MainVehicle.IsSirenActive)
{
MainVehicle.IsSirenActive = SireneActive;
}
if (HornActive)
{
if (!_lastHornActive)
{
_lastHornActive = true;
MainVehicle.SoundHorn(99999);
}
}
else if (_lastHornActive)
{
_lastHornActive = false;
MainVehicle.SoundHorn(1);
}
if (MainVehicle.HasRoof && MainVehicle.RoofState!=RoofState)
{
MainVehicle.RoofState=RoofState;
}
Function.Call(Hash.SET_VEHICLE_BRAKE_LIGHTS, MainVehicle.Handle, BrakeLightsOn);
MainVehicle.SetDamageModel(DamageModel);
}
MainVehicle.LockStatus=LockStatus;
if (IsDeluxoHovering)
{
2022-07-17 12:22:11 +08:00
if (!MainVehicle.IsDeluxoHovering())
{
MainVehicle.SetDeluxoHoverState(true);
}
MainVehicle.SetDeluxoWingRatio(DeluxoWingRatio);
}
2022-07-17 12:22:11 +08:00
else if (Model==1483171323)
{
if (MainVehicle.IsDeluxoHovering())
{
MainVehicle.SetDeluxoHoverState(false);
}
}
#endregion
2022-05-22 15:55:26 +08:00
#endregion
2022-07-17 12:22:11 +08:00
if (LastFullSynced>=LastUpdated)
2022-05-22 15:55:26 +08:00
{
#region -- SYNC STATE --
#region -- PASSENGER SYNC --
// check passengers (and driver).
2022-07-09 19:59:17 +08:00
if (_checkSeat)
{
2022-05-22 15:55:26 +08:00
2022-07-09 19:59:17 +08:00
var currentPassengers = MainVehicle.GetPassengers();
2022-05-22 15:55:26 +08:00
2022-07-09 19:59:17 +08:00
lock (Passengers)
2022-05-22 15:55:26 +08:00
{
2022-07-09 19:59:17 +08:00
for (int i = -1; i<MainVehicle.PassengerCapacity; i++)
2022-05-22 15:55:26 +08:00
{
2022-07-09 19:59:17 +08:00
VehicleSeat seat = (VehicleSeat)i;
if (Passengers.ContainsKey(seat))
2022-06-03 13:11:17 +08:00
{
2022-07-09 19:59:17 +08:00
SyncedPed c = Passengers[seat];
if (c?.ID==Main.LocalPlayerID && (RadioStation!=Function.Call<int>(Hash.GET_PLAYER_RADIO_STATION_INDEX)))
{
Util.SetPlayerRadioIndex(RadioStation);
}
if (c?.MainPed!=null&&(!currentPassengers.ContainsKey(i))&&(!c.MainPed.IsBeingJacked)&&(!c.MainPed.IsTaskActive(TaskType.CTaskExitVehicleSeat)))
{
Passengers[seat].MainPed.SetIntoVehicle(MainVehicle, seat);
}
2022-05-22 15:55:26 +08:00
}
2022-07-09 19:59:17 +08:00
else if (!MainVehicle.IsSeatFree(seat))
2022-05-22 15:55:26 +08:00
{
2022-07-09 19:59:17 +08:00
var p = MainVehicle.Occupants.Where(x => x.SeatIndex==seat).FirstOrDefault();
if ((p!=null)&& !p.IsTaskActive(TaskType.CTaskLeaveAnyCar))
2022-05-22 15:55:26 +08:00
{
p.Task.WarpOutOfVehicle(MainVehicle);
}
}
}
}
}
#endregion
if (Flags.HasVehFlag(VehicleDataFlags.Repaired))
{
MainVehicle.Repair();
}
2022-05-22 15:55:26 +08:00
if (Colors != null && Colors != _lastVehicleColors)
{
Function.Call(Hash.SET_VEHICLE_COLOURS, MainVehicle, Colors[0], Colors[1]);
_lastVehicleColors = Colors;
}
MainVehicle.EngineHealth=EngineHealth;
if (Mods != null && !Mods.Compare(_lastVehicleMods))
{
Function.Call(Hash.SET_VEHICLE_MOD_KIT, MainVehicle, 0);
foreach (KeyValuePair<int, int> mod in Mods)
{
MainVehicle.Mods[(VehicleModType)mod.Key].Index = mod.Value;
}
_lastVehicleMods = Mods;
}
2022-06-20 10:27:45 +08:00
if (Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, MainVehicle)!=LicensePlate)
{
2022-07-20 17:50:01 +08:00
Function.Call(Hash.SET_VEHICLE_NUMBER_PLATE_TEXT, MainVehicle, LicensePlate);
2022-06-20 10:27:45 +08:00
}
2022-07-09 22:18:00 +08:00
if (_lastLivery!=Livery)
{
Function.Call(Hash.SET_VEHICLE_LIVERY, MainVehicle, Livery);
_lastLivery=Livery;
}
2022-05-22 15:55:26 +08:00
#endregion
}
LastUpdated=Main.Ticked;
}
2022-07-30 12:39:09 +08:00
void DisplayVehicle(float calibrationMultiplier=1)
2022-07-17 19:19:23 +08:00
{
var current = MainVehicle.ReadPosition();
var predicted = Position+Velocity*(Networking.Latency+0.001f*LastSyncedStopWatch.ElapsedMilliseconds);
2022-07-29 22:35:20 +08:00
var dist = current.DistanceTo(Position);
if (dist<8)
2022-07-17 19:19:23 +08:00
{
2022-07-30 12:39:09 +08:00
MainVehicle.Velocity = Velocity;
MainVehicle.ApplyForce(calibrationMultiplier*dist*(predicted - current));
2022-07-17 19:19:23 +08:00
if (IsFlipped)
{
MainVehicle.Quaternion=Quaternion.Slerp(MainVehicle.ReadQuaternion(), Quaternion, 0.5f);
2022-07-17 19:19:23 +08:00
MainVehicle.RotationVelocity=RotationVelocity;
}
else
{
Vector3 cali = GetCalibrationRotation();
if (cali.Length()<50)
{
2022-07-30 12:39:09 +08:00
MainVehicle.RotationVelocity = calibrationMultiplier*(RotationVelocity+cali*0.2f);
2022-07-17 19:19:23 +08:00
}
else
{
MainVehicle.Quaternion=Quaternion;
MainVehicle.RotationVelocity=RotationVelocity;
}
}
}
else
{
MainVehicle.Position=predicted;
MainVehicle.Velocity=Velocity;
MainVehicle.Quaternion=Quaternion;
}
}
2022-05-22 15:55:26 +08:00
private Vector3 GetCalibrationRotation()
{
2022-07-20 17:50:01 +08:00
var rot = Quaternion.LookRotation(Quaternion*Vector3.RelativeFront, Quaternion*Vector3.RelativeTop).ToEulerAngles();
var curRot = Quaternion.LookRotation(MainVehicle.ReadQuaternion()*Vector3.RelativeFront, MainVehicle.ReadQuaternion()*Vector3.RelativeTop).ToEulerAngles();
2022-07-20 17:50:01 +08:00
2022-06-17 15:26:27 +08:00
var r = (rot-curRot).ToDegree();
2022-05-22 15:55:26 +08:00
if (r.X>180) { r.X=r.X-360; }
2022-07-20 17:50:01 +08:00
else if (r.X<-180) { r.X=360+r.X; }
2022-05-22 15:55:26 +08:00
if (r.Y>180) { r.Y=r.Y-360; }
else if (r.Y<-180) { r.Y=360+r.Y; }
if (r.Z>180) { r.Z=r.Z-360; }
else if (r.Z<-180) { r.Z=360+r.Z; }
return r;
2022-07-20 17:50:01 +08:00
2022-05-22 15:55:26 +08:00
}
private bool CreateVehicle()
2022-05-22 15:55:26 +08:00
{
MainVehicle?.Delete();
MainVehicle = Util.CreateVehicle(Model, Position);
if (!Model.IsInCdImage)
{
// GTA.UI.Notification.Show($"~r~(Vehicle)Model ({CurrentVehicleModelHash}) cannot be loaded!");
return false;
}
else if (MainVehicle==null)
2022-05-22 15:55:26 +08:00
{
Model.Request();
return false;
2022-05-22 15:55:26 +08:00
}
lock (EntityPool.VehiclesLock)
{
EntityPool.Add(this);
2022-05-22 15:55:26 +08:00
}
2022-06-03 14:40:41 +08:00
MainVehicle.Quaternion = Quaternion;
2022-05-22 15:55:26 +08:00
if (MainVehicle.HasRoof)
{
2022-06-19 13:36:23 +08:00
MainVehicle.RoofState=RoofState;
2022-05-22 15:55:26 +08:00
}
2022-07-02 17:14:56 +08:00
if (IsInvincible) { MainVehicle.IsInvincible=true; }
2022-07-30 12:39:09 +08:00
_isMotorcycle=Model.IsMotorcycle;
Model.MarkAsNoLongerNeeded();
return true;
2022-05-22 15:55:26 +08:00
}
#region -- PEDALING --
/*
* Thanks to @oldnapalm.
*/
private string PedalingAnimDict()
{
switch ((VehicleHash)Model)
2022-05-22 15:55:26 +08:00
{
case VehicleHash.Bmx:
return "veh@bicycle@bmx@front@base";
case VehicleHash.Cruiser:
return "veh@bicycle@cruiserfront@base";
case VehicleHash.Scorcher:
return "veh@bicycle@mountainfront@base";
default:
return "veh@bicycle@roadfront@base";
}
}
private string PedalingAnimName(bool fast)
{
return fast ? "fast_pedal_char" : "cruise_pedal_char";
}
private void StartPedalingAnim(bool fast)
{
MainVehicle.Driver?.Task.PlayAnimation(PedalingAnimDict(), PedalingAnimName(fast), 8.0f, -8.0f, -1, AnimationFlags.Loop | AnimationFlags.AllowRotation, 1.0f);
}
private void StopPedalingAnim(bool fast)
{
MainVehicle.Driver.Task.ClearAnimation(PedalingAnimDict(), PedalingAnimName(fast));
}
#endregion
#region OUTGOING
internal float LastNozzleAngle { get; set; }
internal float LastEngineHealth { get; set; }
2022-07-30 12:39:09 +08:00
internal Vector3 LastVelocity { get; set; }
internal Stopwatch LastSentStopWatch { get; set; }=new Stopwatch();
#endregion
2022-05-22 15:55:26 +08:00
}
}