C#:
using Newtonsoft.Json;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Rust;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using UnityEngine;
using Steamworks;
///Скачано с дискорд сервера Rust Edit [PRO+]
///discord.gg/9vyTXsJyKR
namespace Oxide.Plugins {
[Info("Vehicle Towing", "discord.gg/9vyTXsJyKR", "1.0.9")]
[Description("Allows towing of vehicles with cars.")]
class VehicleTow : RustPlugin {
#region CONFIG
private class MeAnchor {
[JsonProperty("X")]
public float X;
[JsonProperty("Y")]
public float Y;
[JsonProperty("Width")]
public float W;
[JsonProperty("Height")]
public float H;
}
private class NTF {
public static class C {
[JsonIgnore]
public static string Red = "<color=#E77471>";
[JsonIgnore]
public static string ChRed = "<color=#C24641>";
[JsonIgnore]
public static string Green = "<color=#6AFB92>";
[JsonIgnore]
public static string Blue = "<color=#82CAFF>";
[JsonIgnore]
public static string Beige = "<color=#F5F5DC>";
}
public static void SendMessage(BasePlayer player, string input, Dictionary<string, string> replacements = null, bool all = false) {
if (string.IsNullOrEmpty(input)) return;
string output = replacements == null ? input : replacements.Aggregate(input, (current, replacement) => current.Replace(replacement.Key, replacement.Value));
if (all) if (player != null) BasePlayer.activePlayerList.Where(pl => pl != player).ToList().ForEach(pl => pl.ChatMessage(output));
else Instance.Server.Broadcast(output);
else if (Instance.IsConn(player)) player?.ChatMessage(output);
}
[JsonProperty("Max Allowed Load - [name], [max]")]
public string MaxAllow = $"{C.Blue}[name]</color> -> {C.Red}Max Allowed Load:</color> {C.ChRed}[max]</color>";
[JsonProperty("Max Distance - [name], [current], [max], [marked]")]
public string MaxDistance = $"{C.Blue}[name]</color> <- {C.Red}[current]m</color>/{C.Green}[max]m</color> -> {C.Blue}[marked]</color>";
[JsonProperty("Set Behind - [name]")]
public string SetBehind = $"{C.Blue}[name]</color> SET -> {C.Green}BEHIND</color>";
[JsonProperty("Roof Block - [name]")]
public string RoofBlock = $"{C.Blue}[name]</color> Placing onto Vehicle Roofs is {C.Red}Not Allowed</color>";
[JsonProperty("Set On Top - [name]")]
public string SetOnTop = $"{C.Blue}[name]</color> SET -> {C.Beige}ON TOP</color>";
[JsonProperty("Not Vehicle - [name]")]
public string NotVehicle = $"{C.Blue}[name]</color> -> {C.Red}Not Vehicle</color>";
[JsonProperty("Invalid Vehicle - [name]")]
public string InvlidVehicle = $"{C.Blue}[name]</color> -> {C.Red}Invalid Vehicle</color>";
[JsonProperty("Vehicle Locked - [name]")]
public string VehicleLocked = $"{C.Blue}[name]</color> -> {C.Red}Vehicle is Locked</color>";
[JsonProperty("Vehicle Detached - [name]")]
public string Detached = $"{C.Blue}[name]</color> -> {C.Green}DETACHED</color>";
[JsonProperty("Vehicle Not Detachable - [name]")]
public string NotDetachable = $"{C.Blue}[name]</color> -> {C.Red}Not Detachable</color>";
[JsonProperty("Marked Vehicle - [name]")]
public string Marked = $"{C.Blue}[name]</color> -> {C.Beige}MARKED</color>";
[JsonProperty("Pickup Marked Vehicle - [name]")]
public string PickupMarked = $"{C.Blue}[name]</color> -> {C.Beige}PICKUP MARKED</color>";
[JsonProperty("Destroyed Vehicle - [name]")]
public string Destroyed = $"{C.Blue}[name]</color> -> {C.Red}Destroyed</color>";
[JsonProperty("Already Attached Vehicle - [name]")]
public string AAttached = $"{C.Blue}[name]</color> -> {C.Red}Already attached</color>";
[JsonProperty("Can't Attach Vehicle - [name], [marked]")]
public string CantAttach = $"{C.Blue}[marked]</color> -> {C.Red}Can't be attached to</color> {C.Beige}[name]</color>";
[JsonProperty("Chassis Created - [chassis]")]
public string ChassisCr = $"{C.Blue}[chassis]</color> -> {C.Green}Created</color>";
[JsonProperty("Can't Afford Item - [at], [need], [item], [have]")]
public string CantAfford = $"|Option [at]| <color=#C24641>Can't Afford</color>: {C.Blue}x[need] [item]</color> -> {C.Red}Have x[have]</color>\n";
[JsonProperty("Chassis Create Confirm")]
public string ChassisConf = $"{C.Beige}Attach</color> -> Hit the ground 1 more time for chassis spawn";
[JsonProperty("No Permissions")]
public string NoPermissions = $"{C.Red}No Permission</color> to use this item!";
[JsonProperty("Attach Cancelled")]
public string AttachCanc = $"{C.Beige}Attach</color> -> {C.Red}Cancelled</color>";
[JsonProperty("Invalid Ground Hit - [name]")]
public string InvGround = $"{C.Blue}[name]</color> -> {C.Red}Not Ground</color>";
}
private Configuration Settings;
private class Configuration {
[JsonProperty("BEHIND - Max Vehicles")]
public int MaxBehind;
[JsonProperty("BEHIND - Max Distance for Attaching")]
public float MaxBDist;
[JsonProperty("ON TOP - Max Vehicles")]
public int MaxTop;
[JsonProperty("ON TOP - Max Distance for Attaching")]
public float MaxOTDist;
[JsonProperty("ON TOP - Allow Placing onto Vehicle Roofs")]
public bool RoofTop;
[JsonProperty("Max Tilt Angle before Detaching - Default 50")]
public float MaxTilt;
[JsonProperty("Hammer Skin - Default 833855159")]
public ulong HmSKin;
[JsonProperty("Locked Vehicles Requires Access")]
public bool VhLock = true;
[JsonProperty("Hammer Cost - shortName: ammount (Any Combination : EMPTY = FREE)")]
public List<Dictionary<string, int>> HmCost;
[JsonProperty("2 Module Chassis Cost - shortName: amount (Any Combination : EMPTY = FREE)")]
public List<Dictionary<string, int>> MD2Cost;
[JsonProperty("3 Module Chassis Cost - shortName: amount (Any Combination : EMPTY = FREE)")]
public List<Dictionary<string, int>> MD3Cost;
[JsonProperty("4 Module Chassis Cost - shortName: amount (Any Combination : EMPTY = FREE)")]
public List<Dictionary<string, int>> MD4Cost;
[JsonProperty("User Permissions - EMPTY = FREE")]
public List<string> Perms;
[JsonProperty("Item Images URL - Don't edit if you dont have a valid link")]
public string imageURL;
[JsonProperty("Tow Hammer Menu Anchor")]
public MeAnchor THAnchor;
[JsonProperty("Exclusion List - Forbidden Entities")]
public List<string> Exclusion = new List<string>();
[JsonProperty("Chassis Pickup Only Owner")]
public bool ChassisOwner = true;
[JsonProperty("Chassis Pickup Resources - shortName: amount")]
public Dictionary<string, Dictionary<string, int>> ChassisRet = new Dictionary<string, Dictionary<string, int>>() {
{
"car_chassis_2module.entity", new Dictionary<string, int>() {
{
"vehicle.chassis.2mod", 1
}
}
}
, {
"car_chassis_3module.entity", new Dictionary<string, int>() {
{
"vehicle.chassis.3mod", 1
}
}
}
, {
"car_chassis_4module.entity", new Dictionary<string, int>() {
{
"vehicle.chassis.4mod", 1
}
}
}
}
;
[JsonProperty("Game Notifications - '' = disabled")]
public NTF Ntf = new NTF();
public static Configuration Generate() {
return new Configuration {
MaxBehind = 3, MaxBDist = 5f, MaxTop = 3, MaxOTDist = 5f, RoofTop = true, MaxTilt = 50f, HmSKin = 833855159, HmCost = new List<Dictionary<string, int>>() {
new Dictionary<string, int>() {
{
"metal.fragments", 50
}
, {
"metalpipe", 1
}
}
}
, MD2Cost = new List<Dictionary<string, int>>() {
new Dictionary<string, int>() {
{
"piston1", 1
}
, {
"crankshaft1", 1
}
}
, new Dictionary<string, int>() {
{
"piston2", 1
}
, {
"crankshaft2", 1
}
}
, new Dictionary<string, int>() {
{
"piston3", 1
}
, {
"crankshaft3", 1
}
}
}
, MD3Cost = new List<Dictionary<string, int>>() {
new Dictionary<string, int>() {
{
"piston1", 2
}
, {
"crankshaft1", 1
}
}
, new Dictionary<string, int>() {
{
"piston2", 2
}
, {
"crankshaft2", 1
}
}
, new Dictionary<string, int>() {
{
"piston3", 2
}
, {
"crankshaft3", 1
}
}
}
, MD4Cost = new List<Dictionary<string, int>>() {
new Dictionary<string, int>() {
{
"piston1", 2
}
, {
"crankshaft1", 2
}
}
, new Dictionary<string, int>() {
{
"piston2", 2
}
, {
"crankshaft2", 2
}
}
, new Dictionary<string, int>() {
{
"piston3", 2
}
, {
"crankshaft3", 2
}
}
}
, Perms = new List<string>() {
"vehicletow.user"
}
, imageURL = "https://rustlabs.com/img/items180/{itemShortname}.png", THAnchor = new MeAnchor() {
X = 0.652f, Y = 0.026f, W = 0.1f, H = 0.05f
}
}
;
}
}
protected override void SaveConfig() => Config.WriteObject(Settings);
protected override void LoadDefaultConfig() {
Settings = Configuration.Generate();
SaveConfig();
}
protected override void LoadConfig() {
base.LoadConfig();
try {
Settings = Config.ReadObject<Configuration>();
if (Settings == null) LoadDefaultConfig();
SaveConfig();
}
catch {
PrintError("Error reading config, please check!");
}
}
#endregion
#region DATA
[PluginReference]
Plugin ImageLibrary;
private const string _IName = "Tow Hammer";
private readonly int _Mask = LayerMask.GetMask("Terrain", "World");
private readonly string[] ChassisPrefabs = new string[3] {
"assets/content/vehicles/modularcar/car_chassis_2module.entity.prefab", "assets/content/vehicles/modularcar/car_chassis_3module.entity.prefab", "assets/content/vehicles/modularcar/car_chassis_4module.entity.prefab"
}
;
private static readonly DamageTypeList _EmptyDmgList = new DamageTypeList();
static VehicleTow Instance;
#endregion
#region UI
class UI {
public static Hash<string, string> ItemURLcache = new Hash<string, string>();
public static readonly string[] PanelStatic = {
"EngineMenu", "AttachMenu"
}
;
public static CuiElementContainer _cUIs = new CuiElementContainer();
public static void Clean() {
ItemURLcache.Clear();
foreach (BasePlayer player in BasePlayer.activePlayerList) foreach (string Panel in PanelStatic) CuiHelper.DestroyUi(player, Panel);
}
public static string GetImage(int itemID, ulong skinID = 0) => GetImage(ItemManager.FindItemDefinition(itemID)?.shortname ?? "", skinID);
public static string GetImage(string shortname, ulong skinID = 0) => GetItemIconURL(shortname, skinID);
private static string GetItemIconURL(string shortname, ulong skinID = 0) {
if (skinID != 0 && Instance.ImageLibrary != null) {
ItemURLcache[shortname] = Instance.ImageLibrary.Call<string>("GetImageURL", shortname, skinID);
if (string.IsNullOrEmpty(ItemURLcache[shortname])) ItemURLcache[shortname] = MakeURL(shortname);
}
else {
ItemURLcache[shortname] = MakeURL(shortname);
}
return ItemURLcache[shortname];
}
private static string MakeURL(string itemShortname) {
string URL = Instance.Settings.imageURL;
return URL.Replace("{itemShortname}", itemShortname);
}
static CuiRectTransformComponent Normalize(float[] XYWH, bool Linked) {
CuiRectTransformComponent RectComp = new CuiRectTransformComponent();
if (Linked) {
float x = XYWH[0], y = XYWH[1], w = XYWH[2], h = XYWH[3];
XYWH[0] = (1280f * x) - 1280f;
XYWH[1] = 720f * (y + h);
XYWH[2] = (1280f * (x + w)) - 1280f;
XYWH[3] = 720f * y;
RectComp = new CuiRectTransformComponent {
AnchorMin = "1 0", AnchorMax = "1 0", OffsetMin = $"{XYWH[0]} {XYWH[3]}", OffsetMax = $"{XYWH[2]} {XYWH[1]}"
}
;
}
else {
XYWH[2] += XYWH[0];
XYWH[3] += XYWH[1];
RectComp = new CuiRectTransformComponent {
AnchorMin = $"{XYWH[0]} {XYWH[1]}", AnchorMax = $"{XYWH[2]} {XYWH[3]}"
}
;
}
return RectComp;
}
public static CuiElementContainer CreateElementContainer(string panel, string color, float x, float y, float width, float height, bool cursor = false, bool blur = false, string parent = "Overlay", float fadein = 0.4f, bool Linked = false) {
CuiRectTransformComponent RectComp = Normalize(new float[4] {
x, y, width, height
}
, Linked);
CuiImageComponent img;
if (blur) {
//assets/content/ui/uibackgroundblur.mat
img = new CuiImageComponent {
Color = color, FadeIn = fadein, Material = "assets/content/ui/uibackgroundblur-ingamemenu.mat"
}
;
}
else {
img = new CuiImageComponent {
Color = color, FadeIn = fadein
}
;
}
CuiElementContainer NewElement = new CuiElementContainer() {
new CuiElement {
Name = panel, Parent = parent, Components = {
img, RectComp
}
}
, {
new CuiPanel {
CursorEnabled = cursor, Image = img
}
, panel, ""
}
}
;
return NewElement;
}
public static void CreatePanel(ref CuiElementContainer element, string panel, string color, float x, float y, float width, float height, string name = "", bool cursor = false, float fadein = 0.4f) {
string aMin = x.ToString() + " " + y.ToString();
string aMax = (x + width).ToString() + " " + (y + height).ToString();
element.Add(new CuiPanel {
Image = {
Color = color, FadeIn = fadein
}
, RectTransform = {
AnchorMin = aMin, AnchorMax = aMax
}
, CursorEnabled = cursor
}
, panel, name);
}
public static void CreateLabel(ref CuiElementContainer element, string panel, string color, string text, int size, float x, float y, float width, float height, string name = "", TextAnchor align = TextAnchor.MiddleCenter, float fadein = 0.4f) {
string aMin = x.ToString() + " " + y.ToString();
string aMax = (x + width).ToString() + " " + (y + height).ToString();
element.Add(new CuiLabel {
Text = {
Color = color, FontSize = size, Font = "RobotoCondensed-Regular.ttf", Align = align, FadeIn = fadein, Text = text
}
, RectTransform = {
AnchorMin = aMin, AnchorMax = aMax
}
}
, panel, name);
}
public static void CreateTextPanel(ref CuiElementContainer element, string panel, string color, string text, int size, float x, float y, float width, float height, string textColor, string name = "", TextAnchor align = TextAnchor.MiddleCenter, float fadein = 0.4f) {
CreatePanel(ref element, panel, color, x, y, width, height, name, false, fadein);
CreateLabel(ref element, panel, textColor, text, size, x, y, width, height, name, align);
}
public static void CreateButton(ref CuiElementContainer element, string panel, string color, string text, int size, float x, float y, float width, float height, string command, string tColor = "1 1 1 1", string name = "", TextAnchor align = TextAnchor.MiddleCenter, float fadein = 0.4f) {
string aMin = x.ToString() + " " + y.ToString();
string aMax = (x + width).ToString() + " " + (y + height).ToString();
element.Add(new CuiButton {
Button = {
Color = color, Command = command, FadeIn = fadein
}
, RectTransform = {
AnchorMin = aMin, AnchorMax = aMax
}
, Text = {
Text = text, FontSize = size, Align = align, Color = tColor
}
}
, panel, name);
}
public static void CreateImgButton(ref CuiElementContainer element, string panel, string color, string text, int size, float x, float y, float width, float height, string command, string png, string name = "", string opacity = "1", string tColor = "1 1 1 1", TextAnchor align = TextAnchor.MiddleCenter) {
string aMin = x.ToString() + " " + y.ToString();
string aMax = (x + width).ToString() + " " + (y + height).ToString();
element.Add(new CuiElement {
Name = name, Parent = panel, Components = {
new CuiRawImageComponent {
Url = png, Color = "1 1 1 " + opacity, FadeIn = 0.4f
}
, new CuiRectTransformComponent {
AnchorMin = aMin, AnchorMax = aMax
}
}
}
);
element.Add(new CuiButton {
Button = {
Color = color, Command = command, FadeIn = 0.4f
}
, RectTransform = {
AnchorMin = aMin, AnchorMax = aMax
}
, Text = {
Text = text, FontSize = size, Align = align, Color = tColor
}
}
, panel, name);
}
public static void CreateImage(ref CuiElementContainer element, string panel, string png, float x, float y, float width, float height, string name = "", string opacity = "1", float fadein = 0.4f) {
string aMin = x.ToString() + " " + y.ToString();
string aMax = (x + width).ToString() + " " + (y + height).ToString();
element.Add(new CuiElement {
Parent = panel, Components = {
new CuiRawImageComponent {
Url = png, Color = "1 1 1 " + opacity, FadeIn = fadein
}
, new CuiRectTransformComponent {
AnchorMin = aMin, AnchorMax = aMax
}
,
}
, Name = name
}
);
}
public static void CreateTextImage(ref CuiElementContainer element, string panel, string png, float x, float y, float width, float height, string color, string text, int size, string name = "", string opacity = "1") {
string aMin = x.ToString() + " " + y.ToString();
string aMax = (x + width).ToString() + " " + (y + height).ToString();
CuiElement imgCUI = new CuiElement {
Parent = panel, Components = {
new CuiRawImageComponent {
Url = png, Color = "1 1 1 " + opacity
}
, new CuiRectTransformComponent {
AnchorMin = aMin, AnchorMax = aMax
}
,
}
, Name = name
}
;
element.Add(imgCUI);
element.Add(new CuiLabel {
Text = {
Color = color, FontSize = size, Font = "RobotoCondensed-regular.ttf", Align = TextAnchor.LowerCenter, FadeIn = 0.4f, Text = text
}
, RectTransform = {
AnchorMin = aMin, AnchorMax = aMax
}
}
, imgCUI.Name);
}
public static string Color(string hexColor, float alpha) {
if (hexColor.StartsWith("#")) hexColor = hexColor.TrimStart('#');
int red = int.Parse(hexColor.Substring(0, 2), NumberStyles.AllowHexSpecifier);
int green = int.Parse(hexColor.Substring(2, 2), NumberStyles.AllowHexSpecifier);
int blue = int.Parse(hexColor.Substring(4, 2), NumberStyles.AllowHexSpecifier);
return $"{(double)red / 255} {(double)green / 255} {(double)blue / 255} {alpha}";
}
}
void SetupStatic() {
// Engine UI
CuiElementContainer _cUI = UI.CreateElementContainer(UI.PanelStatic[0], UI.Color("#808080", 0.1f), 0.652f, 0.32f, 0.047f, 0.08f, false, true, "Overlay", 0, true);
UI.CreateImgButton(ref _cUI, UI.PanelStatic[0], UI.Color("#808080", 0), _IName, 10, 0.05f, 0.05f, 0.90f, 0.90f, "GiveTowHammer", UI.GetImage("hammer.salvaged", Instance.Settings.HmSKin), "", "1", UI.Color("#FFFFFF", 1), TextAnchor.LowerCenter);
UI.CreatePanel(ref _cUI, UI.PanelStatic[0], UI.Color("#808080", 0), 1.05f, 0, 2, 1, "TowItemCost");
float[] newPos = Instance.GetNewPosY(new float[] {
0, 0.6f, 0, 0, 0.25f, 0.4f, 0.05f, 0.15f
}
);
foreach (KeyValuePair<string, int> entry in Instance.Settings.HmCost[0]) {
if (newPos == null) break;
UI.CreateImage(ref _cUI, "TowItemCost", UI.GetImage(entry.Key), newPos[2], newPos[3], newPos[4], newPos[5]);
UI.CreateLabel(ref _cUI, "TowItemCost", UI.Color("#FFFFFF", 1), $"x{entry.Value}", 10, newPos[2], newPos[3], newPos[4], newPos[5], "", TextAnchor.LowerRight);
newPos = Instance.GetNewPosY(newPos);
}
UI._cUIs = _cUI;
}
void UpdateEngine(BasePlayer player) {
CuiHelper.DestroyUi(player, UI.PanelStatic[0]);
CuiHelper.AddUi(player, UI._cUIs);
}
void UpdateAttach(BasePlayer player) {
CuiHelper.DestroyUi(player, UI.PanelStatic[1]);
if (!WagonMonitor.plCreate.ContainsKey(player)) WagonMonitor.plCreate[player] = new Dictionary<int, int>() {
{
0, 1
}
}
;
MeAnchor A = Settings.THAnchor;
CuiElementContainer _cUI = UI.CreateElementContainer(UI.PanelStatic[1], UI.Color("#808080", 0.1f), A.X, A.Y, A.W, A.H, false, true, "Overlay", 0, true);
int id = WagonMonitor.plCreate[player].FirstOrDefault().Key;
string[] chassis = new string[3] {
"Small Chassis", "Medium Chassis", "Large Chassis"
}
;
UI.CreateLabel(ref _cUI, UI.PanelStatic[1], UI.Color("#FFFFFF", 1), $"{chassis[id]} [R]", 15, 0, 0, 1, 1);
UI.CreatePanel(ref _cUI, UI.PanelStatic[1], UI.Color("#808080", 0), 0, 1.05f, 1, 2, "ChassisItemCost");
List<Dictionary<string, int>> Data = id == 2 ? Instance.Settings.MD4Cost : id == 1 ? Instance.Settings.MD3Cost : Instance.Settings.MD2Cost;
float[] newPos = Instance.GetNewPosY(new float[] {
0.06f, 0.6f, 0, 0, 0.25f, 0.4f, 0.05f, 0.05f
}
);
int at = 1;
foreach (Dictionary<string, int> dict in Data) {
foreach (KeyValuePair<string, int> entry in dict) {
if (newPos == null) break;
UI.CreateLabel(ref _cUI, "ChassisItemCost", UI.Color("#82CAFF", 1), $"{at}", 10, newPos[2], newPos[3], newPos[4], newPos[5], "", TextAnchor.UpperLeft);
UI.CreateImage(ref _cUI, "ChassisItemCost", UI.GetImage(entry.Key), newPos[2], newPos[3], newPos[4], newPos[5]);
UI.CreateLabel(ref _cUI, "ChassisItemCost", UI.Color("#FFFFFF", 1), $"x{entry.Value}", 10, newPos[2], newPos[3], newPos[4], newPos[5], "", TextAnchor.LowerRight);
newPos = Instance.GetNewPosY(newPos);
}
++at;
}
CuiHelper.AddUi(player, _cUI);
}
#endregion
#region CLASSES
class WagonMonitor : MonoBehaviour {
public static Dictionary<BaseEntity, BaseEntity> childParent = new Dictionary<BaseEntity, BaseEntity>();
public static Dictionary<BasePlayer, BaseEntity> plSett = new Dictionary<BasePlayer, BaseEntity>();
public static Dictionary<BasePlayer, BaseEntity> plPick = new Dictionary<BasePlayer, BaseEntity>();
public static Dictionary<BasePlayer, Dictionary<int, int>> plCreate = new Dictionary<BasePlayer, Dictionary<int, int>>();
static bool IsInvalid(ModularCar modular) => modular?.HasFlag(BaseEntity.Flags.Reserved6) ?? false;
public static void CleanData(BasePlayer player) {
if (plSett.ContainsKey(player)) plSett.Remove(player);
if (plCreate.ContainsKey(player)) plCreate.Remove(player);
}
static BaseEntity getPlMark(BasePlayer player) => plSett.ContainsKey(player) ? plSett[player] : null;
public static BaseEntity getPickMark(BasePlayer player) => plPick.ContainsKey(player) ? plPick[player] : null;
static object[] getLastChild(BaseVehicle parent, BaseEntity child, bool chassis, BasePlayer player) {
int atChild = 1;
int atParent = 1;
int debug = 100;
int linked = 1;
IEnumerable<BaseEntity> children = parent.children.Where(ch => chassis && ch is ModularCar || !chassis && !(ch is ModularCar) && ch is BaseVehicle);
while (children.Count() > 0 && (debug -= 1) > 0) {
atChild++;
parent = children.LastOrDefault() as BaseVehicle;
children = parent.children.Where(ch => chassis && ch is ModularCar || !chassis && !(ch is ModularCar) && ch is BaseVehicle);
}
linked = atChild;
int maxAtt = chassis ? Instance.Settings.MaxBehind : Instance.Settings.MaxTop;
if (atChild <= maxAtt && chassis) {
debug = 100;
BaseEntity _parent = parent?.GetParentEntity();
while (_parent != null && (debug -= 1) > 0) {
atParent++;
_parent = _parent?.GetParentEntity();
}
linked = atParent;
if (atParent <= maxAtt) {
debug = 100;
atParent = 1 + (--atChild);
children = child.children.Where(ch => chassis && ch is ModularCar || !chassis && !(ch is ModularCar) && ch is BaseVehicle);
while (children.Count() > 0 && (debug -= 1) > 0) {
atParent++;
_parent = children.LastOrDefault() as BaseVehicle;
children = _parent.children.Where(ch => chassis && ch is ModularCar || !chassis && !(ch is ModularCar) && ch is BaseVehicle);
}
linked = atParent;
}
}
bool canAttach = atChild <= maxAtt && atParent <= maxAtt;
if (!canAttach) {
string eName = parent.GetType().FullName;
NTF.SendMessage(player, Instance.Settings.Ntf.MaxAllow, new Dictionary<string, string>() {
{
"[name]", eName
}
, {
"[max]", maxAtt.ToString()
}
}
);
CleanData(player);
return new object[] {
null, linked
}
;
}
return new object[] {
parent, linked
}
;
}
static void TrySetChassis(BaseVehicle parent, BaseEntity child, BasePlayer player) {
string eName = child.GetType().FullName;
if (parent is ModularCar && child is ModularCar) {
object[] getLink = getLastChild(parent, child, true, player);
parent = getLink[0] as BaseVehicle;
if (parent.IsUnityNull()) return;
string eMark = parent.GetType().FullName;
float distance = Vector3.Distance(parent.transform.position, child.transform.position);
// Get Distance of Last Child
float alDist = Instance.Settings.MaxBDist;
if (distance > alDist) {
NTF.SendMessage(player, Instance.Settings.Ntf.MaxDistance, new Dictionary<string, string>() {
{
"[name]", eName
}
, {
"[current]", distance.ToString("0.00")
}
, {
"[max]", alDist.ToString()
}
, {
"[marked]", eMark
}
}
);
CleanData(player);
return;
}
child.gameObject.AddComponent<WagonMonitor>().Setup(parent, (int)getLink[1]);
NTF.SendMessage(player, Instance.Settings.Ntf.SetBehind, new Dictionary<string, string>() {
{
"[name]", eName
}
}
);
}
else {
if ((parent is ModularCar) && (parent as ModularCar).NumAttachedModules > 0 && !Instance.Settings.RoofTop) {
NTF.SendMessage(player, Instance.Settings.Ntf.RoofBlock, new Dictionary<string, string>() {
{
"[name]", eName
}
}
);
CleanData(player);
return;
}
float distance = Vector3.Distance(parent.transform.position, child.transform.position);
// Get Distance of Chassis
object[] getLink = getLastChild(parent, child, false, player);
parent = getLink[0] as BaseVehicle;
if (parent.IsUnityNull()) return;
string eMark = parent.GetType().FullName;
float alDist = Instance.Settings.MaxOTDist;
if (distance > alDist) {
NTF.SendMessage(player, Instance.Settings.Ntf.MaxDistance, new Dictionary<string, string>() {
{
"[name]", eName
}
, {
"[current]", distance.ToString("0.00")
}
, {
"[max]", alDist.ToString()
}
, {
"[marked]", eMark
}
}
);
CleanData(player);
return;
}
child.gameObject.AddComponent<WagonMonitor>().Setup(parent, (int)getLink[1], true);
NTF.SendMessage(player, Instance.Settings.Ntf.SetOnTop, new Dictionary<string, string>() {
{
"[name]", eName
}
}
);
}
CleanData(player);
if (childParent.ContainsKey(child)) childParent.Remove(child);
childParent.Add(child, parent);
}
public static void TrySetting(BasePlayer player, BaseEntity entity) {
entity = entity?.GetParentEntity() != null && (!(entity is BaseVehicle) || entity is BaseVehicleModule) ? entity.GetParentEntity() : entity;
string eName = entity.GetType().FullName;
if (!(entity is BaseVehicle) || entity is BaseVehicleModule) {
NTF.SendMessage(player, Instance.Settings.Ntf.NotVehicle, new Dictionary<string, string>() {
{
"[name]", eName
}
}
);
return;
}
ModularCar modular = entity as ModularCar;
if (IsInvalid(modular) || Instance.Settings.Exclusion.Contains(eName, StringComparer.OrdinalIgnoreCase)) {
NTF.SendMessage(player, Instance.Settings.Ntf.InvlidVehicle, new Dictionary<string, string>() {
{
"[name]", eName
}
}
);
return;
}
if (modular != null && Instance.Settings.VhLock && !modular.PlayerHasUnlockPermission(player)) {
NTF.SendMessage(player, Instance.Settings.Ntf.VehicleLocked, new Dictionary<string, string>() {
{
"[name]", eName
}
}
);
return;
}
if (plSett.ContainsKey(player) && plSett[player] == entity) {
bool detach = Instance.DetachEntity(entity);
string txt = detach ? Instance.Settings.Ntf.Detached : Instance.Settings.Ntf.NotDetachable ;
NTF.SendMessage(player, txt, new Dictionary<string, string>() {
{
"[name]", eName
}
}
);
CleanData(player);
return;
}
BaseEntity markedENT = getPlMark(player);
if (!(markedENT is BaseEntity)) {
plSett[player] = entity;
NTF.SendMessage(player, Instance.Settings.Ntf.Marked, new Dictionary<string, string>() {
{
"[name]", eName
}
}
);
return;
}
string eMark = markedENT.GetType().FullName;
if (markedENT.IsUnityNull() || markedENT.IsDestroyed) {
NTF.SendMessage(player, Instance.Settings.Ntf.Destroyed, new Dictionary<string, string>() {
{
"[name]", eName
}
}
);
CleanData(player);
return;
}
if (childParent.ContainsKey(markedENT) || childParent.ContainsKey(entity) && childParent[entity] == markedENT || childParent.ContainsKey(entity) && !(entity is ModularCar) && markedENT is ModularCar || entity == markedENT) {
NTF.SendMessage(player, Instance.Settings.Ntf.AAttached, new Dictionary<string, string>() {
{
"[name]", eName
}
}
);
CleanData(player);
return;
}
if (entity is ModularCar && markedENT is ModularCar || entity is ModularCar && markedENT is BaseVehicle) {
TrySetChassis((entity as BaseVehicle), markedENT, player);
}
else if (entity is BaseVehicle && markedENT is ModularCar) {
TrySetChassis((markedENT as BaseVehicle), entity, player);
}
else {
NTF.SendMessage(player, Instance.Settings.Ntf.CantAttach, new Dictionary<string, string>() {
{
"[name]", eName
}
, {
"[marked]", eMark
}
}
);
CleanData(player);
return;
}
}
bool READY = false;
bool onTop;
float angularDrag;
int links = 1;
float updSpeed = 3f;
float angleTilt = 50f;
BaseEntity parent;
ModularCar parentCar;
BaseEntity child;
ModularCar chassis;
Rigidbody rigidbody;
Vector3 local;
Vector3 localElur => rigidbody.transform.eulerAngles;
public float getAngDrag => angularDrag;
float GetTop() {
float Y = child is ScrapTransportHelicopter ? 2.85f : parent is ModularCar && (parent as ModularCar).NumAttachedModules <= 0 ? 0.85f : 0;
return child.bounds.size.y - Y;
}
public void Setup(BaseEntity pr, int linked, bool top = false) {
parent = pr;
parentCar = pr as ModularCar;
onTop = top;
child = gameObject?.ToBaseEntity();
angleTilt = Instance.Settings.MaxTilt;
links = linked;
if (onTop) {
local = Vector3.up * GetTop();
SetOnTop();
}
else if (child is ModularCar) {
chassis = child as ModularCar;
string name = chassis.ShortPrefabName;
float at = name.Contains("4module") ? child.bounds.size.z + 0.5f : name.Contains("3module") ? child.bounds.size.z + (parent.bounds.size.z / 3f) : child.bounds.size.z + (parent.bounds.size.z / 2f);
local = Vector3.back * at;
SetBehind();
}
Instance.NextTick(() => {
child.transform.parent = null;
child.transform.SetParent(parent.transform, true);
}
);
READY = true;
}
void OnDestroy() {
if (parent.IsUnityNull() || parent.IsDestroyed) {
GameManager.server.CreateEntity(child.PrefabName, child.transform.position, child.transform.rotation).Spawn();
}
childParent.Remove(child);
}
void SetOnTop() {
transform.localPosition = local;
child.SetParent(parent);
rigidbody = GetComponent<Rigidbody>();
angularDrag = rigidbody.angularDrag;
rigidbody.transform.eulerAngles = new Vector3(0, parent.transform.eulerAngles.y, 0);
rigidbody.constraints = RigidbodyConstraints.FreezePositionX | RigidbodyConstraints.FreezePositionZ | RigidbodyConstraints.FreezePositionY;
rigidbody.angularDrag = 0;
}
void SetBehind() {
transform.localPosition = local;
child.SetParent(parent);
rigidbody = GetComponent<Rigidbody>();
angularDrag = rigidbody.angularDrag;
rigidbody.transform.eulerAngles = new Vector3(localElur.x, parent.transform.eulerAngles.y, localElur.z);
rigidbody.constraints = RigidbodyConstraints.FreezePositionX | RigidbodyConstraints.FreezePositionZ | RigidbodyConstraints.FreezeRotationY;
rigidbody.angularDrag = 0;
}
float WrapAngle(float angle) {
angle %= 360;
if (angle > 180) return angle - 360;
return angle;
}
bool IsTilted() {
Vector3 eAngle = child.transform.rotation.eulerAngles;
float angleX = WrapAngle(eAngle.x);
float angleZ = WrapAngle(eAngle.z);
return angleX > angleTilt || angleZ > angleTilt || angleX < -angleTilt || angleZ < -angleTilt;
}
void FixedUpdate() {
if (parent.IsUnityNull() || parent.IsDestroyed) Destroy(this);
if (!READY) return;
if (IsTilted() || IsInvalid(chassis)) {
Instance.DetachEntity(child);
return;
}
if (onTop) {
transform.localPosition = local;
transform.rotation = Quaternion.Euler(new Vector3(localElur.x, parent.transform.eulerAngles.y, localElur.z));
}
else if (parentCar.IsMoving()) {
transform.localPosition = new Vector3(local.x, transform.localPosition.y, local.z);
Quaternion elur = Quaternion.Euler(new Vector3(localElur.x, parent.transform.eulerAngles.y, localElur.z));
float angle = Quaternion.Angle(transform.rotation, elur);
rigidbody.MoveRotation(Quaternion.RotateTowards(transform.rotation, elur, Time.deltaTime * updSpeed * angle));
}
}
}
#endregion
#region METHODS
bool DetachEntity(BaseEntity entity) {
WagonMonitor monitor;
if (!entity.TryGetComponent(out monitor)) return false;
float angularDrag = monitor.getAngDrag;
UnityEngine.Object.Destroy(monitor);
Rigidbody rigidbody = entity.GetComponent<Rigidbody>();
rigidbody.constraints = RigidbodyConstraints.None;
rigidbody.angularDrag = angularDrag;
entity.SetParent(null, true);
if (WagonMonitor.childParent.ContainsKey(entity)) WagonMonitor.childParent.Remove(entity);
return true;
}
void CreateChassis(BasePlayer player, V
// ... [Code preview truncated due to length. Full source in attached file] ...