C#:
//Reference: UnityEngine.VehiclesModule
using System;
using System.Collections;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Configuration;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using UnityEngine;
using Rust;
using System.Linq;
using System.Globalization;
namespace Oxide.Plugins {
[Info("CarCommander", "k1lly0u", "0.2.72")]
[Description("A custom car controller with many options including persistence")]
class CarCommander : RustPlugin {
#region Fields
[PluginReference]
Plugin Clans, Friends, Spawns, RandomSpawns;
RestoreData storedData;
private Dictionary<ulong, double> userCooldowns = new Dictionary<ulong, double>();
private DynamicConfigFile data, cooldowns;
private static CarCommander ins;
private Dictionary<string, string> itemNames = new Dictionary<string, string>();
private Dictionary<ulong, HotwireManager> isHotwiring = new Dictionary<ulong, HotwireManager>();
private Dictionary<CommandType, BUTTON> controlButtons;
private List<CarController> temporaryCars = new List<CarController>();
private List<CarController> saveableCars = new List<CarController>();
private bool initialized;
private bool wipeData = false;
private int fuelType;
private int repairType;
private string fuelTypeName;
private string repairTypeName;
const string carPrefab = "assets/content/vehicles/sedan_a/sedantest.entity.prefab";
const string boxPrefab = "assets/prefabs/deployable/woodenbox/woodbox_deployed.prefab";
const string explosionPrefab = "assets/prefabs/npc/m2bradley/effects/bradley_explosion.prefab";
const string uiHealth = "CCUI_Health";
const string uiFuel = "CCUI_Fuel";
#endregion
#region Oxide Hooks
private void Loaded() {
permission.RegisterPermission("carcommander.admin", this);
permission.RegisterPermission("carcommander.use", this);
permission.RegisterPermission("carcommander.canspawn", this);
permission.RegisterPermission("carcommander.canbuild", this);
permission.RegisterPermission("carcommander.ignorecooldown", this);
lang.RegisterMessages(Messages, this);
data = Interface.Oxide.DataFileSystem.GetFile("carcommander_data");
cooldowns = Interface.Oxide.DataFileSystem.GetFile("carcommander_cooldowns");
}
private void OnServerInitialized() {
ins = this;
LoadData();
ConvertControlButtons();
fuelType = ItemManager.itemList.Find(x => x.shortname == configData.Fuel.FuelType)?.itemid ?? 0;
repairType = ItemManager.itemList.Find(x => x.shortname == configData.Repair.Shortname)?.itemid ?? 0;
fuelTypeName = ItemManager.itemList.Find(x => x.shortname == configData.Fuel.FuelType)?.displayName.english ?? "Invalid fuel shortname set in config!";
repairTypeName = ItemManager.itemList.Find(x => x.shortname == configData.Repair.Shortname)?.displayName.english ?? "Invalid repair item shortname set in config!";
initialized = true;
if (wipeData) {
PrintWarning("Map wipe detected! Wiping previous car data");
storedData.restoreData.Clear();
SaveData();
}
timer.In(3, RestoreVehicleInventories);
}
private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info) {
if (entity == null || info == null) return;
if (entity.GetComponent<CarController>()) entity.GetComponent<CarController>().ManageDamage(info);
else if (entity.GetComponent<StorageContainer>()) {
if (entity.GetParentEntity()?.GetComponent<CarController>()) NullifyDamage(info);
}
}
private void OnHammerHit(BasePlayer player, HitInfo info) {
if (player == null || info == null || info.HitEntity == null || !configData.Repair.Enabled) return;
CarController controller = info.HitEntity.GetComponent<CarController>();
if (controller != null && controller.entity != null) {
if (controller.entity.health < controller.entity.MaxHealth()) {
if (player.inventory.GetAmount(repairType) >= configData.Repair.Amount) {
player.inventory.Take(null, repairType, configData.Repair.Amount);
controller.entity.Heal(configData.Repair.Damage);
player.Command("note.inv", new object[] {
repairType, configData.Repair.Amount * -1
}
);
}
else SendReply(player, string.Format(msg("noresources", player.UserIDString), configData.Repair.Amount, repairTypeName));
}
else SendReply(player, msg("fullhealth", player.UserIDString));
}
}
private void OnItemDeployed(Deployer deployer, BaseEntity entity) {
if (!configData.Security.Trunk.RequireKey) return;
BasePlayer player = deployer.GetOwnerPlayer();
if (player == null || entity == null) return;
CarController controller = entity.GetComponentInParent<CarController>();
if (controller != null) {
Deployable deployable = deployer.GetDeployable();
if (deployable != null) {
if (deployable.prefabID == 3518824735 || deployable.prefabID == 2106860026) {
if (controller.HasVehicleKey(player)) return;
entity.GetSlot(deployable.slot)?.Kill();
if (deployable.prefabID == 3518824735) player.GiveItem(ItemManager.CreateByItemID(1159991980, 1, 0));
// Codelock
else player.GiveItem(ItemManager.CreateByItemID(-850982208, 1, 0));
SendReply(player, msg("no_key_deploy", player.UserIDString));
}
}
}
}
private void OnPlayerInput(BasePlayer player, InputState input) {
if (!initialized || player == null) return;
if (input.WasJustPressed(BUTTON.USE)) {
RaycastHit hit;
if (Physics.SphereCast(player.eyes.position, 0.5f, Quaternion.Euler(player.serverInput.current.aimAngles) * Vector3.forward, out hit, 3f)) {
CarController controller = hit.GetEntity()?.GetComponent<CarController>();
if (controller != null && controller.HasCommander() && !controller.occupants.Contains(player)) CanMountEntity(player, controller.entity);
}
return;
}
if (configData.Fuel.Enabled && input.WasJustPressed(controlButtons[CommandType.FuelTank])) {
RaycastHit hit;
if (Physics.SphereCast(player.eyes.position, 0.5f, Quaternion.Euler(player.serverInput.current.aimAngles) * Vector3.forward, out hit, 3f)) {
CarController controller = hit.GetEntity()?.GetComponent<CarController>();
if (controller != null && !controller.HasCommander() && !controller.occupants.Contains(player)) OpenInventory(player, controller, controller.fuelTank);
}
return;
}
}
private void OnEntityKill(BaseNetworkable networkable) {
BasicCar BasicCar = networkable.GetComponent<BasicCar>();
if (BasicCar != null) {
if (storedData.HasRestoreData(BasicCar.net.ID)) storedData.RemoveData(BasicCar.net.ID);
CarController controller = BasicCar.GetComponent<CarController>();
if (controller != null) {
saveableCars.Remove(controller);
temporaryCars.Remove(controller);
}
}
}
private void OnNewSave(string filename) => wipeData = true;
private void OnServerSave() {
for (int i = saveableCars.Count - 1; i >= 0; i--) {
CarController controller = saveableCars[i];
if (controller == null || controller.entity == null || !controller.entity.IsValid() || controller.entity.IsDestroyed) {
saveableCars.RemoveAt(i);
continue;
}
storedData.AddData(controller);
}
SaveData();
}
private void Unload() {
CarController[] objects = saveableCars.Union(temporaryCars).ToArray();
for (int i = 0; i < objects.Length; i++) UnityEngine.Object.Destroy(objects[i]);
objects = UnityEngine.Object.FindObjectsOfType<CarController>();
if (objects != null) {
foreach (CarController obj in objects) UnityEngine.Object.Destroy(obj);
}
ins = null;
}
private object CanPickupEntity(BaseCombatEntity entity, BasePlayer player) {
if (entity.GetComponentInParent<CarController>()) return false;
return null;
}
private void OnPlayerDisconnected(BasePlayer player) {
if (isHotwiring.ContainsKey(player.userID)) isHotwiring[player.userID].CancelHotwire();
if (configData.Decay.InstantDecay) ServerMgr.Instance.StartCoroutine(DelayedDestroy(player.userID));
}
private IEnumerator DelayedDestroy(ulong playerId) {
List<CarController> controllers = Facepunch.Pool.GetList<CarController>();
controllers.AddRange(saveableCars);
controllers.AddRange(temporaryCars);
for (int i = controllers.Count - 1; i >= 0; i--) {
CarController controller = controllers[i];
if (controller?.ownerId == playerId) {
BasicCar BasicCar = controller?.entity;
UnityEngine.Object.Destroy(controller);
if (BasicCar != null && !BasicCar.IsDestroyed) BasicCar.Kill();
}
yield return null;
}
Facepunch.Pool.FreeList(ref controllers);
}
private void OnEntityMounted(BaseMountable mountable, BasePlayer player) {
CarController controller = mountable.VehicleParent()?.GetComponent<CarController>();
if (controller != null) controller.OnEntityMounted(player, mountable == controller.entity.mountPoints[0].mountable);
}
private void OnEntityDismounted(BaseMountable mountable, BasePlayer player) {
CarController controller = mountable.VehicleParent()?.GetComponent<CarController>();
if (controller != null) {
if (controller.Commander?.userID == player.userID) {
if (isHotwiring.ContainsKey(player.userID)) {
isHotwiring[player.userID].CancelHotwire();
SendReply(player, ins.msg("hotwire_fail_left", player.UserIDString));
}
}
controller.OnEntityDismounted(player, mountable);
}
}
private object CanMountEntity(BasePlayer player, BaseMountable mountable) {
CarController controller = mountable.VehicleParent()?.GetComponent<CarController>();
if (controller != null) {
if (player.isMounted) return false;
if (controller.externallyManaged) return false;
if (controller.isDieing) return false;
if (controller.IsFlipped) return false;
if (!permission.UserHasPermission(player.UserIDString, "carcommander.use")) {
SendReply(player, msg("nopermission", player.UserIDString));
return false;
}
if (mountable == controller.entity.mountPoints[0].mountable) return null;
else {
if (configData.Passengers.Enabled) {
BasePlayer commander = controller.Commander;
if (commander == null) return null;
else {
if (!configData.Passengers.UseFriends && !configData.Passengers.UseClans) return null;
if (configData.Passengers.UseFriends && AreFriends(commander.userID, player.userID)) return null;
if (configData.Passengers.UseClans && IsClanmate(commander.userID, player.userID)) return null;
SendReply(player, msg("not_friend", player.UserIDString));
return false;
}
}
else {
SendReply(player, msg("not_enabled", player.UserIDString));
return false;
}
}
}
return null;
}
private object CanDismountEntity(BasePlayer player, BaseMountable mountable) {
CarController controller = mountable.VehicleParent()?.GetComponent<CarController>();
if (controller != null) {
if (controller.externallyManaged) return null;
// Temporary solution for failed dismount
controller.DismountPlayer(player, mountable);
return false;
}
return null;
}
#endregion
#region Functions
private T ParseType<T>(string type) {
try {
return (T)Enum.Parse(typeof(T), type, true);
}
catch {
PrintError($"INVALID CONFIG OPTION DETECTED! The value \"{type}\" is an incorrect selection.\nAvailable options are: {Enum.GetNames(typeof(T)).ToSentence()}");
return default(T);
}
}
private bool HasPermission(BasePlayer player, string perm) => permission.UserHasPermission(player.UserIDString, perm) || permission.UserHasPermission(player.UserIDString, "Carcommander.admin");
private void ConvertControlButtons() {
controlButtons = new Dictionary<CommandType, BUTTON> {
[CommandType.Accelerate]
= ParseType<BUTTON>(configData.Buttons.Accelerate), [CommandType.Brake] = ParseType<BUTTON>(configData.Buttons.Brake), [CommandType.Left] = ParseType<BUTTON>(configData.Buttons.Left), [CommandType.Right] = ParseType<BUTTON>(configData.Buttons.Right), [CommandType.Handbrake] = ParseType<BUTTON>(configData.Buttons.HBrake), [CommandType.Lights] = ParseType<BUTTON>(configData.Buttons.Lights), [CommandType.FuelTank] = ParseType<BUTTON>(configData.Buttons.FuelTank)
}
;
}
private void NullifyDamage(HitInfo info) {
info.damageTypes = new DamageTypeList();
info.HitEntity = null;
info.HitMaterial = 0;
info.PointStart = Vector3.zero;
}
private void OpenInventory(BasePlayer player, CarController controller, ItemContainer container) {
player.inventory.loot.Clear();
player.inventory.loot.entitySource = controller.entity;
player.inventory.loot.itemSource = null;
player.inventory.loot.AddContainer(container);
player.inventory.loot.SendImmediate();
player.ClientRPCPlayer(null, player, "RPC_OpenLootPanel", "generic");
player.SendNetworkUpdate();
}
private void RestoreVehicleInventories() {
if (storedData.restoreData.Count > 0) {
BaseEntity[] objects = BaseEntity.saveList.Where(x => x is BasicCar).ToArray();
if (objects != null) {
foreach (BaseEntity obj in objects) {
if (obj == null || !obj.IsValid() || obj.IsDestroyed) continue;
if (storedData.HasRestoreData(obj.net.ID)) {
obj.gameObject.AddComponent<CarController>();
}
}
}
}
CheckForSpawns();
}
private void CheckForSpawns() {
if (configData.Spawnable.Enabled) {
if (saveableCars.Count < configData.Spawnable.Max) {
object position = null;
if (configData.Spawnable.RandomSpawns) {
if (!RandomSpawns) {
PrintError("RandomSpawns can not be found! Unable to autospawn cars");
return;
}
object success = RandomSpawns.Call("GetSpawnPoint");
if (success != null) position = (Vector3)success;
else PrintError("Unable to find a valid spawnpoint from RandomSpawns");
}
else {
if (!Spawns) {
PrintError("Spawns Database can not be found! Unable to autospawn cars");
return;
}
object success = Spawns.Call("GetSpawnsCount", configData.Spawnable.Spawnfile);
if (success is string) {
PrintError("An invalid spawnfile has been set in the config.Unable to autospawn cars : " + (string)success);
return;
}
success = Spawns.Call("GetRandomSpawn", configData.Spawnable.Spawnfile);
if (success is string) {
PrintError((string)success);
return;
}
else position = (Vector3)success;
}
if (position != null) {
List<BasicCar> entities = Facepunch.Pool.GetList<BasicCar>();
Vis.Entities((Vector3)position, 5f, entities);
if (entities.Count > 0) {
timer.In(10, CheckForSpawns);
return;
}
else SpawnAtLocation((Vector3)position, new Quaternion(), true);
Facepunch.Pool.FreeList(ref entities);
}
}
timer.In(configData.Spawnable.Time, CheckForSpawns);
}
}
private int CountPlayerVehicles(ulong playerId) {
int count = 0;
for (int i = 0; i < saveableCars.Count; i++) {
if (saveableCars[i].ownerId == playerId) count++;
}
for (int i = 0; i < temporaryCars.Count; i++) {
if (temporaryCars[i].ownerId == playerId) count++;
}
return count;
}
private double GrabCurrentTime() => DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
private string FormatTime(double time) {
TimeSpan dateDifference = TimeSpan.FromSeconds((float)time);
int days = dateDifference.Days;
int hours = dateDifference.Hours;
hours += (days * 24);
int mins = dateDifference.Minutes;
int secs = dateDifference.Seconds;
return string.Format("{0:00}:{1:00}:{2:00}", hours, mins, secs);
}
#endregion
#region API
private BaseEntity SpawnAtLocation(Vector3 position, Quaternion rotation = default(Quaternion), bool enableSaving = false, bool isExternallyManaged = false, bool repairEnabled = true, bool disableFuel = false, bool disableSecurity = false, bool disableCollision = false) {
BaseEntity entity = GameManager.server.CreateEntity(carPrefab, position + Vector3.up, rotation);
entity.enableSaving = enableSaving;
entity.Spawn();
CarController controller = entity.gameObject.AddComponent<CarController>();
if (enableSaving) {
saveableCars.Add(controller);
storedData.AddData(controller);
SaveData();
}
else temporaryCars.Add(controller);
if (isExternallyManaged) controller.SetExternallyManaged();
else controller.SetFeatures(repairEnabled, disableFuel, disableSecurity, disableCollision);
return entity;
}
private void ToggleController(BasicCar BasicCar, bool enabled) {
if (BasicCar == null) return;
CarController controller = BasicCar.GetComponent<CarController>();
if (controller != null) {
controller.enabled = enabled;
if (!enabled) {
foreach (BasicCar.VehicleWheel wheel in controller.entity.wheels) wheel.wheelCollider.brakeTorque = 1f;
}
}
}
private void MountPlayerTo(BasePlayer player, BasicCar BasicCar) {
if (player == null) return;
CarController controller = BasicCar.GetComponent<CarController>();
if (controller != null) {
BaseMountable mountable = controller.entity.mountPoints[0].mountable;
if (mountable != null && mountable.GetMounted() == null) {
player.EnsureDismounted();
mountable._mounted = player;
player.MountObject(mountable, 0);
player.MovePosition(mountable.mountAnchor.transform.position);
player.transform.rotation = mountable.mountAnchor.transform.rotation;
player.ServerRotation = mountable.mountAnchor.transform.rotation;
player.OverrideViewAngles(mountable.mountAnchor.transform.rotation.eulerAngles);
player.eyes.NetworkUpdate(mountable.mountAnchor.transform.rotation);
player.ClientRPCPlayer<Vector3>(null, player, "ForcePositionTo", player.transform.position);
mountable.SetFlag(BaseEntity.Flags.Busy, true, false);
controller.OnEntityMounted(player, true);
}
}
}
private void EjectAllPlayers(BasicCar BasicCar) {
CarController controller = BasicCar.GetComponent<CarController>();
if (controller != null) controller.EjectAllPlayers();
}
private ItemContainer GetVehicleInventory(BasicCar BasicCar) {
CarController controller = BasicCar.GetComponent<CarController>();
if (controller == null) return null;
return controller.container.inventory;
}
private ItemContainer GetVehicleFuelTank(BasicCar BasicCar) {
CarController controller = BasicCar.GetComponent<CarController>();
if (controller == null) return null;
return controller.fuelTank;
}
#endregion
#region Component
enum CommandType {
Accelerate, Brake, Left, Right, Handbrake, FuelTank, Lights
}
public class CarController : MonoBehaviour {
#region Variables
public BasicCar entity;
public StorageContainer container;
public ItemContainer fuelTank;
private Rigidbody rb;
private Dictionary<CommandType, BUTTON> cb;
private ConfigData.SecurityOptions security;
private ConfigData.CollisionOptions collision;
private ConfigData.DecaySettings decay;
public List<BasePlayer> occupants = new List<BasePlayer>();
WheelCollider[] allWheels = new WheelCollider[4];
WheelFrictionCurve sidewaysFriction = new WheelFrictionCurve();
WheelFrictionCurve sidewaysFrictionHB = new WheelFrictionCurve();
public ulong ownerId = 0UL;
private float engineTorque;
private float brakeTorque;
private float reverseTorque;
private float speed;
private float maxSpeed;
private bool useDefaultHandling;
private float maxSteeringAngleSpeed;
private float maxSteeringAngle;
private bool applyCounterSteer;
private float driftAngle;
private bool isDrifting;
public float antiRollFrontHorizontal = 5000f;
public float antiRollRearHorizontal = 5000f;
public float antiRollVertical = 500f;
private float accelInput = 0f;
private float brakeInput = 0f;
private float steerInput = 0f;
private bool repairEnabled;
private bool fuelEnabled;
private float consumptionRate;
private int fuelId;
private float pendingFuel;
private float nextFuelCheckTime;
private bool hasFuel = false;
private bool isFlipped;
private float upsideDownTime;
private int ignitionCode;
private bool hasBeenHotwired;
private bool ignitionOn;
private bool eBrake;
private bool driftFriction;
public bool externallyManaged;
public bool isDieing;
public bool lightsOn;
// Temporary solution for dismounting players
private Vector3[] dismountLocals = new Vector3[] {
new Vector3(-1.4f, 0.1f, 0.5f), new Vector3(1.4f, 0.1f, 0.5f), new Vector3(-1.4f, 0.1f, -0.5f), new Vector3(1.4f, 0.1f, -0.5f)
}
;
public BasePlayer Commander {
get {
return entity.mountPoints[0].mountable.GetMounted();
}
}
public bool HasBeenHotwired {
get {
return hasBeenHotwired;
}
set {
hasBeenHotwired = value;
}
}
public int IgnitionCode {
get {
return ignitionCode;
}
set {
ignitionCode = value;
}
}
public bool IsFlipped {
get {
return isFlipped;
}
}
#endregion
#region Initialization
private void Awake() {
entity = GetComponent<BasicCar>();
entity.enabled = false;
//if (entity.IsMounted())
// entity.DismountAllPlayers();
EjectAllPlayers();
SetWheelColliders();
InitializeSettings();
InitializeFuel();
InitializeInventory();
InitializeDecay();
}
private void SetWheelColliders() {
if (!ins.configData.Movement.CustomHandling) {
useDefaultHandling = true;
return;
}
allWheels = new WheelCollider[] {
entity.wheels[0].wheelCollider, entity.wheels[1].wheelCollider, entity.wheels[2].wheelCollider, entity.wheels[3].wheelCollider
}
;
WheelFrictionCurve forwardFriction = new WheelFrictionCurve();
forwardFriction.extremumSlip = 0.2f;
forwardFriction.extremumValue = 1;
forwardFriction.asymptoteSlip = 0.8f;
forwardFriction.asymptoteValue = 0.75f;
forwardFriction.stiffness = 1.5f;
sidewaysFriction.extremumSlip = 0.2f;
sidewaysFriction.extremumValue = 0.8f;
sidewaysFriction.asymptoteSlip = 0.4f;
sidewaysFriction.asymptoteValue = 0.6f;
sidewaysFriction.stiffness = 1.0f;
sidewaysFrictionHB.extremumSlip = 0.15f;
sidewaysFrictionHB.extremumValue = 0.7f;
sidewaysFrictionHB.asymptoteSlip = 0.2f;
sidewaysFrictionHB.asymptoteValue = 0.5f;
sidewaysFrictionHB.stiffness = 0.75f;
ConfigData.MovementSettings movement = ins.configData.Movement;
foreach (WheelCollider wc in allWheels) {
JointSpring spring = wc.suspensionSpring;
spring.spring = movement.Spring;
spring.damper = movement.Damper;
spring.targetPosition = movement.Target;
wc.suspensionSpring = spring;
wc.suspensionDistance = movement.Distance;
wc.forceAppPointDistance = 0.1f;
wc.mass = 40f;
wc.wheelDampingRate = 1f;
wc.sidewaysFriction = sidewaysFriction;
wc.forwardFriction = forwardFriction;
}
}
private void InitializeSettings() {
rb = entity.GetComponent<Rigidbody>();
cb = ins.controlButtons;
security = ins.configData.Security;
collision = ins.configData.Collision;
ConfigData.MovementSettings movement = ins.configData.Movement;
engineTorque = movement.Acceleration;
brakeTorque = movement.Brakes;
reverseTorque = movement.Reverse;
maxSteeringAngle = movement.Steer;
maxSteeringAngleSpeed = movement.SteerSpeed;
applyCounterSteer = movement.CounterSteer;
antiRollFrontHorizontal = movement.AntiRollFH;
antiRollRearHorizontal = movement.AntiRollRH;
antiRollVertical = movement.AntiRollV;
maxSpeed = movement.Speed;
repairEnabled = ins.configData.Repair.Enabled;
entity.mountPoints[0].mountable.canWieldItems = !ins.configData.ActiveItems.DisableDriver;
for (int i = 1; i < entity.mountPoints.Count; i++) entity.mountPoints[i].mountable.canWieldItems = !ins.configData.ActiveItems.DisablePassengers;
}
private void InitializeFuel() {
ConfigData.FuelOptions fuel = ins.configData.Fuel;
fuelId = ins.fuelType;
fuelEnabled = fuel.Enabled;
consumptionRate = fuel.Consumption;
if (fuelEnabled) {
fuelTank = new ItemContainer();
fuelTank.ServerInitialize(null, 1);
if ((int)fuelTank.uid == 0) fuelTank.GiveUID();
fuelTank.onlyAllowedItems = new ItemDefinition[] {
ItemManager.itemList.Find(x => x.itemid == fuelId)
}
;
if (fuel.GiveFuel && !ins.storedData.HasRestoreData(entity.net.ID)) {
Item fuelItem = ItemManager.CreateByItemID(fuelId, UnityEngine.Random.Range(fuel.FuelAmountMin, fuel.FuelAmountMax));
fuelItem.MoveToContainer(fuelTank);
}
}
hasFuel = fuelEnabled ? GetFuelAmount() < 1 : false;
}
private void InitializeInventory() {
if (ins.configData.Inventory.Enabled) {
container = GameManager.server.CreateEntity(boxPrefab, entity.transform.position, entity.transform.rotation) as StorageContainer;
container.enableSaving = false;
container.skinID = (ulong)1195832261;
container.Spawn();
Destroy(container.GetComponent<DestroyOnGroundMissing>());
Destroy(container.GetComponent<GroundWatch>());
Destroy(container.GetComponent<BoxCollider>());
container.SetParent(entity);
container.transform.localPosition = new Vector3(0, 0.475f, -2.15f);
container.transform.localRotation = Quaternion.Euler(-10, 180, 0);
container.panelName = "generic_resizable";
container.inventorySlots = ins.configData.Inventory.Size;
container.isLockable = ins.configData.Security.Trunk.CanLock;
container.pickup.enabled = false;
container.inventory = new ItemContainer();
container.inventory.ServerInitialize(null, ins.configData.Inventory.Size);
if ((int)container.inventory.uid == 0) container.inventory.GiveUID();
}
if (ins.storedData.HasRestoreData(entity.net.ID)) {
ins.storedData.RestoreVehicle(this);
ins.saveableCars.Add(this);
}
}
private void InitializeDecay() {
decay = ins.configData.Decay;
StartDecayTimer();
}
#endregion
#region Input
private void FixedUpdate() {
if (WaterLevel.Factor(entity.WorldSpaceBounds().ToBounds()) > 0.7f) {
isDieing = true;
enabled = false;
if (externallyManaged) Interface.CallHook("OnVehicleUnderwater", entity);
else StopToDie();
return;
}
CheckUpsideDown();
hasFuel = fuelEnabled ? HasFuel(false) : true;
BasePlayer player = Commander;
if (useDefaultHandling) {
if (player == null || !hasFuel || !ignitionOn) entity.NoDriverInput();
DoSteering();
ApplyForceAtWheels();
if (player?.serverInput.WasJustPressed(cb[CommandType.Lights]) ?? false) ToggleLights();
entity.SetFlag(BaseEntity.Flags.Reserved1, entity.AnyMounted() && hasFuel && ignitionOn, false);
entity.SetFlag(BaseEntity.Flags.Reserved2, (!entity.AnyMounted() ? false : lightsOn), false);
if (fuelEnabled && ignitionOn) UseFuel(Time.deltaTime * (entity.gasPedal == 0 ? 0.0333f : 1f));
}
else {
if (player == null || !ignitionOn) {
accelInput = 0f;
brakeInput = 0.5f;
eBrake = true;
}
else {
rb.drag = rb.velocity.magnitude / 250;
if (player.serverInput.WasJustPressed(cb[CommandType.Lights])) ToggleLights();
eBrake = player.serverInput.IsDown(cb[CommandType.Handbrake]);
if (player.serverInput.IsDown(cb[CommandType.Accelerate])) {
accelInput = 1f;
brakeInput = 0f;
}
else if (player.serverInput.IsDown(cb[CommandType.Brake])) {
brakeInput = 1f;
accelInput = 0f;
}
else {
brakeInput = 0f;
accelInput = 0f;
}
steerInput = player.serverInput.IsDown(cb[CommandType.Left]) ? -1f : player.serverInput.IsDown(cb[CommandType.Right]) ? 1f : 0f;
if (fuelEnabled && ignitionOn) UseFuel(Time.deltaTime * (accelInput == 0 ? 0.0333f : 1f));
}
speed = rb.velocity.magnitude * 3.6f;
ApplyAcceleration();
AntiRollBars();
CheckForDrift();
AdjustSteering();
entity.SetFlag(BaseEntity.Flags.Reserved1, player != null && hasFuel && ignitionOn, false);
}
}
#endregion
#region Movement
#region Default Handling
private void ApplyForceAtWheels() {
if (entity.rigidBody == null) return;
Vector3 vector3 = entity.rigidBody.velocity;
float single = vector3.magnitude * Vector3.Dot(vector3.normalized, base.transform.forward);
float single1 = entity.brakePedal;
float single2 = entity.gasPedal;
if (single > 0f && single2 < 0f) {
single1 = 100f;
}
else if (single < 0f && single2 > 0f) {
single1 = 100f;
}
BasicCar.VehicleWheel[] vehicleWheelArray = entity.wheels;
for (int i = 0; i < vehicleWheelArray.Length; i++) {
BasicCar.VehicleWheel vehicleWheel = vehicleWheelArray[i];
if (vehicleWheel.wheelCollider.isGrounded) {
if (vehicleWheel.powerWheel) {
vehicleWheel.wheelCollider.motorTorque = single2 * entity.motorForceConstant;
}
if (vehicleWheel.brakeWheel) {
vehicleWheel.wheelCollider.brakeTorque = single1 * entity.brakeForceConstant;
}
}
}
entity.SetFlag(BaseEntity.Flags.Reserved3, (single1 < 100f ? false : entity.AnyMounted()), false);
}
private void DoSteering() {
BasicCar.VehicleWheel[] vehicleWheelArray = entity.wheels;
for (int i = 0; i < vehicleWheelArray.Length; i++) {
BasicCar.VehicleWheel vehicleWheel = vehicleWheelArray[i];
if (vehicleWheel.steerWheel) {
vehicleWheel.wheelCollider.steerAngle = entity.steering;
}
}
entity.SetFlag(BaseEntity.Flags.Reserved4, entity.steering < -2f, false);
entity.SetFlag(BaseEntity.Flags.Reserved5, entity.steering > 2f, false);
}
#endregion
private void ApplyAcceleration() {
if (accelInput > 0 && speed > maxSpeed) accelInput = 0;
float velocity = rb.velocity.magnitude * Vector3.Dot(rb.velocity.normalized, entity.transform.forward);
float _motorTorque = hasFuel ? engineTorque * accelInput : 0;
float _reverseTorque = hasFuel ? -reverseTorque : 0;
float _brakeTorque = brakeTorque * brakeInput;
if (velocity < 0.01f && brakeInput > 0.5f) {
for (int i = 0; i < allWheels.Length; i++) {
WheelCollider wc = allWheels[i];
wc.brakeTorque = 0;
if (i < 2) wc.motorTorque = _reverseTorque * 0.8f;
else wc.motorTorque = _reverseTorque;
}
}
else {
if (eBrake) {
for (int i = 2; i < allWheels.Length; i++) {
WheelCollider wc = allWheels[i];
wc.motorTorque = 0;
wc.brakeTorque = brakeTorque;
if (steerInput != 0 && allWheels[i].isGrounded) {
rb.angularVelocity = new Vector3(rb.angularVelocity.x, rb.angularVelocity.y + (steerInput / 60f), rb.angularVelocity.z);
wc.sidewaysFriction = sidewaysFrictionHB;
driftFriction = true;
}
}
}
else {
for (int i = 0; i < allWheels.Length; i++) {
WheelCollider wc = allWheels[i];
if (i > 1 && driftFriction) {
wc.sidewaysFriction = sidewaysFriction;
driftFriction = false;
}
wc.motorTorque = _motorTorque;
wc.brakeTorque = _brakeTorque;
}
}
entity.SetFlag(BaseEntity.Flags.Reserved3, Commander == null ? false : (velocity > 0f && accelInput < 0f) || (velocity < 0f && brakeInput > 0f), false);
}
}
private void AdjustSteering() {
float steerAngle = Mathf.Lerp(maxSteeringAngle, maxSteeringAngleSpeed, (speed / maxSpeed)) * steerInput;
if (applyCounterSteer) steerAngle = Mathf.Clamp((steerAngle * (steerInput + driftAngle)), -steerAngle, steerAngle);
allWheels[0].steerAngle = steerAngle;
allWheels[1].steerAngle = steerAngle;
entity.SetFlag(BaseEntity.Flags.Reserved4, steerInput == -1f, false);
entity.SetFlag(BaseEntity.Flags.Reserved5, steerInput == 1f, false);
}
private void AntiRollBars() {
WheelHit FrontWheelHit;
float travelFL = 1.0f;
float travelFR = 1.0f;
bool groundedFL = allWheels[0].GetGroundHit(out FrontWheelHit);
if (groundedFL) travelFL = (-allWheels[0].transform.InverseTransformPoint(FrontWheelHit.point).y - allWheels[0].radius) / allWheels[0].suspensionDistance;
bool groundedFR = allWheels[1].GetGroundHit(out FrontWheelHit);
if (groundedFR) travelFR = (-allWheels[1].transform.InverseTransformPoint(FrontWheelHit.point).y - allWheels[1].radius) / allWheels[1].suspensionDistance;
float antiRollForceFrontHorizontal = (travelFL - travelFR) * antiRollFrontHorizontal;
if (groundedFL) rb.AddForceAtPosition(allWheels[0].transform.up * -antiRollForceFrontHorizontal, allWheels[0].transform.position);
if (groundedFR) rb.AddForceAtPosition(allWheels[1].transform.up * antiRollForceFrontHorizontal, allWheels[1].transform.position);
WheelHit RearWheelHit;
float travelRL = 1.0f;
float travelRR = 1.0f;
bool groundedRL = allWheels[2].GetGroundHit(out RearWheelHit);
if (groundedRL) travelRL = (-allWheels[2].transform.InverseTransformPoint(RearWheelHit.point).y - allWheels[2].radius) / allWheels[2].suspensionDistance;
bool groundedRR = allWheels[3].GetGroundHit(out RearWheelHit);
if (groundedRR) travelRR = (-allWheels[3].transform.InverseTransformPoint(RearWheelHit.point).y - allWheels[3].radius) / allWheels[3].suspensionDistance;
float antiRollForceRearHorizontal = (travelRL - travelRR) * antiRollRearHorizontal;
if (groundedRL) rb.AddForceAtPosition(allWheels[2].transform.up * -antiRollForceRearHorizontal, allWheels[2].transform.position);
if (groundedRR) rb.AddForceAtPosition(allWheels[3].transform.up * antiRollForceRearHorizontal, allWheels[3].transform.position);
float antiRollForceFrontVertical = (travelFL - travelRL) * antiRollVertical;
if (groundedFL) rb.AddForceAtPosition(allWheels[0].transform.up * -antiRollForceFrontVertical, allWheels[0].transform.position);
if (groundedRL) rb.AddForceAtPosition(allWheels[2].transform.up * antiRollForceFrontVertical, allWheels[2].transform.position);
float antiRollForceRearVertical = (travelFR - travelRR) * antiRollVertical;
if (groundedFR) rb.AddForceAtPosition(allWheels[1].transform.up * -antiRollForceRearVertical, allWheels[1].transform.position);
if (groundedRR) rb.AddForceAtPosition(allWheels[3].transform.up * antiRollForceRearVertical, allWheels[3].transform.position);
}
private void CheckForDrift() {
WheelHit hit;
allWheels[3].GetGroundHit(out hit);
if (speed > 1f && isDrifting && !eBrake) driftAngle = hit.sidewaysSlip * 1f;
else driftAngle = 0f;
if (Mathf.Abs(hit.sidewaysSlip) >.25f) isDrifting = true;
else isDrifting = false;
}
private void CheckUpsideDown() {
if (externallyManaged) return;
if (Vector3.Dot(entity.transform.up, Vector3.down) > 0) {
if (!isFlipped) {
upsideDownTime += Time.deltaTime;
if (upsideDownTime > 3f) OnCarFlipped();
}
}
else {
upsideDownTime = 0;
if (isFlipped) isFlipped = false;
}
}
private void OnCarFlipped() {
isFlipped = true;
BasePlayer player = Commander;
if (ins.configData.Repair.CanFlip && player != null) player.ChatMessage(ins.msg("carFlipped", player.UserIDString));
EjectAllPlayers();
}
public void ResetCar() {
entity.GetComponent<Rigidbody>().velocity = Vector3.zero;
entity.transform.rotation = Quaternion.Euler(0, entity.transform.eulerAngles.y, 0);
entity.SendNetworkUpdate();
isFlipped = false;
}
#endregion
#region Collision
private void OnCollisionEnter(Collision col) {
if (!collision.Enabled) return;
bool damage = false;
foreach(ContactPoint cp in col.contacts) {
float local = cp.point.y - entity.transform.position.y;
if (local > 0.4f) {
damage = true;
break;
}
}
if (damage) {
float force = col.relativeVelocity.magnitude;
if (force > 2) entity.Hurt(new HitInfo(null, entity, DamageType.Explosion, force * collision.Multiplier));
}
}
#endregion
#region Decay
private void StartDecayTimer() {
if (decay.Enabled && occupants.Count == 0) InvokeHandler.Invoke(this, DealDecayDamage, decay.Time);
}
private void DealDecayDamage() {
if (entity.IsOutside()) {
float decayDamage = entity.MaxHealth() * (decay.Amount / 100);
if (decayDamage >= entity.health) {
entity.Die();
return;
}
else entity.Hurt(decayDamage, DamageType.Decay, null, false);
}
InvokeHandler.Invoke(this, DealDecayDamage, decay.Time);
}
#endregion
#region Functions
private void ToggleLights() {
lightsOn = !lightsOn;
entity.SetFlag(BaseEntity.Flags.Reserved2, lightsOn, false);
}
private int GetFuelAmount() => fuelTank.GetAmount(fuelId, true);
private bool HasFuel(bool forceCheck = false) {
if (Time.time > nextFuelCheckTime || forceCheck) {
hasFuel = (float)GetFuelAmount() > 0f;
nextFuelCheckTime = Ti
// ... [Code preview truncated due to length. Full source in attached file] ...