C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
// #define DEBUG
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Car Setup", "misticos", "1.0.1")]
[Description("Manage car options easily")]
class CarSetup : CovalencePlugin
{
#region Variables
private static CarSetup _ins = null;
private Dictionary<NetworkableId, BaseController> _controllers = new Dictionary<NetworkableId, BaseController>();
#endregion
#region Configuration
private Configuration _config;
private class Configuration
{
[JsonProperty(PropertyName = "Sedan")]
public SedanConfiguration BasicCar = new SedanConfiguration();
[JsonProperty(PropertyName = "Modular Cars", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<ModularCarConfiguration> ModularCars = new List<ModularCarConfiguration>
{ new ModularCarConfiguration() };
public class ModularCarConfiguration : BaseConfiguration
{
[JsonProperty(PropertyName = "Selector")]
public ModularCarSelector Selector = new ModularCarSelector();
public class ModularCarSelector
{
[JsonProperty(PropertyName = "Modules Available",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public HashSet<int> Modules = new HashSet<int> { 1, 2, 3, 4 };
public bool Fits(ModularCar car)
{
return Modules.Contains(car.TotalSockets);
}
}
}
public class SedanConfiguration : BaseConfiguration
{
[JsonProperty(PropertyName = "Steering Angle")]
public float Steering = 60f;
}
public class BaseConfiguration
{
[JsonProperty(PropertyName = "Mass Multiplier")]
public float MassMultiplier = 1f;
[JsonProperty(PropertyName = "Wheel Colliders")]
public WheelColliderData WheelColliders = new WheelColliderData();
[JsonProperty(PropertyName = "Movement")]
public MovementData Movement = new MovementData();
[JsonProperty(PropertyName = "Flipping")]
public FlipData Flip = new FlipData();
public class FlipData
{
[JsonProperty(PropertyName = "Minimum Rotation")]
public float MinimumRotation = 5f;
[JsonProperty(PropertyName = "Torque Applied")]
public float Torque = 120f;
}
public class MovementData
{
[JsonProperty(PropertyName = "Forward Force")]
public float ForceForward = 200f;
[JsonProperty(PropertyName = "Backward Force")]
public float ForceBackward = 150f;
}
public class WheelColliderData
{
[JsonProperty(PropertyName = "Mass Multiplier")]
public float WheelMass = 1f;
[JsonProperty(PropertyName = "Damping Rate Multiplier")]
public float DampingRate = 1f;
[JsonProperty(PropertyName = "Motor Torque")]
public float TorqueMotor = 400f;
[JsonProperty(PropertyName = "Brake Torque")]
public float TorqueBrake = 10000f;
[JsonProperty(PropertyName = "Suspension")]
public SuspensionData Suspension = new SuspensionData();
public class SuspensionData
{
[JsonProperty(PropertyName = "Extension Distance Multiplier")]
public float ExtensionDistance = 1f;
[JsonProperty(PropertyName = "Damper Force Multiplier")]
public float DamperForce = 1f;
[JsonProperty(PropertyName = "Sprint Force Multiplier")]
public float SpringForce = 1f;
}
public void Apply(WheelCollider wheel)
{
wheel.mass *= WheelMass;
wheel.suspensionDistance *= Suspension.ExtensionDistance;
wheel.wheelDampingRate *= DampingRate;
var suspensionSpring = wheel.suspensionSpring;
suspensionSpring.damper *= Suspension.DamperForce;
suspensionSpring.spring *= Suspension.SpringForce;
wheel.suspensionSpring = suspensionSpring;
}
public void Undo(WheelCollider wheel)
{
wheel.mass /= WheelMass;
wheel.suspensionDistance /= Suspension.ExtensionDistance;
wheel.wheelDampingRate /= DampingRate;
var suspensionSpring = wheel.suspensionSpring;
suspensionSpring.damper /= Suspension.DamperForce;
suspensionSpring.spring /= Suspension.SpringForce;
wheel.suspensionSpring = suspensionSpring;
}
}
}
}
protected override void LoadConfig()
{
base.LoadConfig();
try
{
_config = Config.ReadObject<Configuration>();
if (_config == null) throw new Exception();
SaveConfig();
}
catch
{
PrintError("Your configuration file contains an error.Using default configuration values.");
LoadDefaultConfig();
}
}
protected override void SaveConfig() => Config.WriteObject(_config);
protected override void LoadDefaultConfig() => _config = new Configuration();
#endregion
#region Hooks
private void Init()
{
_ins = this;
}
private void OnServerInitialized()
{
foreach (var entity in BaseNetworkable.serverEntities)
{
var basicCar = entity as BasicCar;
if (!ReferenceEquals(basicCar, null))
{
#if DEBUG
Puts($"BasicCar / {basicCar.net.ID} / {basicCar.transform.position}");
#endif
OnEntitySpawned(basicCar);
}
var car = entity as ModularCar;
if (!ReferenceEquals(car, null))
{
#if DEBUG
Puts($"BasicCar / {car.net.ID} / {car.transform.position}");
#endif
OnEntitySpawned(car);
}
}
}
private void OnEntitySpawned(BasicCar vehicle)
{
vehicle.gameObject.AddComponent<BasicCarController>();
}
private void OnEntitySpawned(ModularCar vehicle)
{
vehicle.gameObject.AddComponent<ModularCarController>();
}
private void Unload()
{
foreach (var vehicle in UnityEngine.Object.FindObjectsOfType<BasicCarController>())
UnityEngine.Object.DestroyImmediate(vehicle);
foreach (var vehicle in UnityEngine.Object.FindObjectsOfType<ModularCarController>())
UnityEngine.Object.DestroyImmediate(vehicle);
_ins = null;
}
private void OnEntityMounted(BaseMountable mountable, BasePlayer player)
{
var uid = mountable.parentEntity.uid;
if (!uid.IsValid)
return;
BaseController controller;
if (!_controllers.TryGetValue(uid, out controller))
return;
controller.Driver = player;
}
private void OnEntityDismounted(BaseMountable mountable, BasePlayer player)
{
var uid = mountable.parentEntity.uid;
if (!uid.IsValid)
return;
BaseController controller;
if (!_controllers.TryGetValue(uid, out controller))
return;
controller.Driver = null;
}
#endregion
#region Controller
[DefaultExecutionOrder(10000)] // Ensure we apply our changes after Facepunch do
private abstract class BaseController : FacepunchBehaviour
{
protected WheelCollider[] Wheels;
protected Rigidbody Rigidbody;
public BasePlayer Driver;
}
private abstract class BaseController<TVehicle, TConfig> : BaseController where TVehicle : BaseVehicle
where TConfig : Configuration.BaseConfiguration
{
protected TVehicle Car;
protected TConfig Base;
private NetworkableId _netId;
protected virtual void Awake()
{
Car = GetComponent<TVehicle>();
Rigidbody = Car.rigidBody;
_ins._controllers[_netId = Car.net.ID] = this;
}
protected virtual void Start()
{
Rigidbody.mass *= Base.MassMultiplier;
if (Wheels == null || Wheels.Length == 0)
{
Debug.LogWarning(
$"There were no wheels found in {nameof(BaseController<TVehicle, TConfig>)}/{nameof(Start)}.");
return;
}
foreach (var wheel in Wheels)
Base.WheelColliders.Apply(wheel);
}
protected void OnDestroy()
{
_ins?._controllers?.Remove(_netId);
if (Base == null)
return;
Rigidbody.mass /= Base.MassMultiplier;
if (Wheels == null || Wheels.Length == 0)
return;
foreach (var wheel in Wheels)
Base.WheelColliders.Undo(wheel);
}
protected virtual void FixedUpdate()
{
ApplyFlip();
ApplyWheelTorque();
ApplyMovement();
}
protected void ApplyFlip()
{
foreach (var wheel in Wheels)
{
if (Math.Abs(wheel.steerAngle) < Base.Flip.MinimumRotation || !wheel.isGrounded)
continue;
Rigidbody.AddRelativeTorque(
new Vector3(0f, 0f, Base.Flip.Torque * Math.Sign(wheel.steerAngle) * -1f), ForceMode.Force);
}
}
protected virtual void ApplyWheelTorque()
{
foreach (var wheel in Wheels)
{
ApplyWheelTorque(wheel);
}
}
protected abstract void ApplyWheelTorque(WheelCollider wheel);
protected void ApplyMovement()
{
// This is before wheels check because it is faster
if (ReferenceEquals(Driver, null))
return;
// Prevent it from pushing you in the sky
foreach (var wheel in Wheels)
{
if (wheel.isGrounded)
continue;
return;
}
if (Driver.serverInput.IsDown(BUTTON.FORWARD))
{
Rigidbody.AddRelativeForce(Vector3.forward * Base.Movement.ForceForward, ForceMode.Impulse);
}
else if (Driver.serverInput.IsDown(BUTTON.BACKWARD))
{
Rigidbody.AddRelativeForce(Vector3.back * Base.Movement.ForceBackward, ForceMode.Impulse);
}
}
}
private class ModularCarController : BaseController<ModularCar, Configuration.ModularCarConfiguration>
{
private VisualCarWheel[] _modularWheels;
protected override void Awake()
{
base.Awake();
foreach (var option in _ins._config.ModularCars)
{
if (!option.Selector.Fits(Car))
continue;
Base = option;
}
if (Base == null)
DestroyImmediate(this);
}
protected override void Start()
{
_modularWheels = new[] { Car.wheelRR, Car.wheelFL, Car.wheelFR, Car.wheelRL };
Wheels = new WheelCollider[_modularWheels.Length];
for (var i = 0; i < Wheels.Length; i++)
Wheels[i] = _modularWheels[i].wheelCollider;
base.Start();
}
protected override void ApplyWheelTorque(WheelCollider wheel)
{
if (Rigidbody.IsSleeping())
return;
// If no driver and not being pushed, then no motor torque
wheel.motorTorque = ReferenceEquals(Driver, null) && Car.timeSinceLastPush > 2f
? 0f
: Base.WheelColliders.TorqueMotor;
// If no driver, not being pushed and low velocity, then brake torque
wheel.brakeTorque = ReferenceEquals(Driver, null) && Rigidbody.velocity.magnitude < 2.5f &&
Car.timeSinceLastPush > 2f
? Base.WheelColliders.TorqueBrake
: 0f;
}
}
private class BasicCarController : BaseController<BasicCar, Configuration.SedanConfiguration>
{
protected override void Awake()
{
base.Awake();
Base = _ins._config.BasicCar;
}
protected override void Start()
{
Wheels = new WheelCollider[Car.wheels.Length];
for (var i = 0; i < Car.wheels.Length; i++)
{
Wheels[i] = Car.wheels[i].wheelCollider;
}
base.Start();
}
protected override void FixedUpdate()
{
base.FixedUpdate();
if (ReferenceEquals(Driver, null))
return;
if (Driver.serverInput.IsDown(BUTTON.LEFT))
{
Car.steering = Base.Steering;
}
if (Driver.serverInput.IsDown(BUTTON.RIGHT))
{
Car.steering = -Base.Steering;
}
}
protected override void ApplyWheelTorque(WheelCollider wheel)
{
if (ReferenceEquals(Driver, null))
return;
wheel.motorTorque = Car.gasPedal * Base.WheelColliders.TorqueMotor;
wheel.brakeTorque = Car.brakePedal * Base.WheelColliders.TorqueBrake;
}
}
#endregion
}
}