C#:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Facepunch;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Rust;
using UnityEngine;
using VLB;
using WebSocketSharp;
using Random = UnityEngine.Random;
namespace Oxide.Plugins
{
[Info("Crafts", "Mevent", "1.17.0")]
public class Crafts : RustPlugin
{
#region Fields
[PluginReference] private Plugin ImageLibrary, SpawnModularCar;
private const string Layer = "UI.Crafts";
private static Crafts _instance;
private enum WorkbenchLevel
{
None = 0,
One = 1,
Two = 2,
Three = 3
}
private enum CraftType
{
Command,
Vehicle,
Item,
Recycler,
ModularCar
}
#endregion
#region Config
private static Configuration _config;
private class Configuration
{
[JsonProperty(PropertyName = "The color of the button when all the items are present")]
public string GreenColor = "#80FF8080";
[JsonProperty(PropertyName = "Command")]
public string Command = "craft";
[JsonProperty(PropertyName = "Enable debug?")]
public bool useDebug = true;
[JsonProperty(PropertyName = "Workbenches Setting",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<WorkbenchLevel, WorkbenchConfig> Workbenchs =
new Dictionary<WorkbenchLevel, WorkbenchConfig>
{
[WorkbenchLevel.None] = new WorkbenchConfig("#00000080", "No Workbench Required"),
[WorkbenchLevel.One] = new WorkbenchConfig("#80400080", "Workbench 1 LVL"),
[WorkbenchLevel.Two] = new WorkbenchConfig("#0080FF80", "Workbench 2 LVL"),
[WorkbenchLevel.Three] = new WorkbenchConfig("#FF000080", "Workbench 3 LVL")
};
[JsonProperty(PropertyName = "Craft Settings", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<CraftConfig> CraftsList = new List<CraftConfig>
{
new CraftConfig
{
Enabled = true,
ImageURL = "https://i.imgur.com/YXjADeE.png",
Description = new List<string>
{
"Craft requires:",
"- Gears (5 pcs)",
"- Road Signs (5 pcs)",
"- Metal (2000 pcs)"
},
Command = "givecopter",
Permission = "crafts.all",
DisplayName = "Minicopter",
ShortName = "electric.flasherlight",
Amount = 1,
SkinID = 2080145158,
Type = CraftType.Vehicle,
Prefab = "assets/content/vehicles/minicopter/minicopter.entity.prefab",
Level = WorkbenchLevel.One,
UseDistance = true,
Distance = 1.5f,
GiveCommand = string.Empty,
Ground = true,
Structure = true,
Items = new List<ItemForCraft>
{
new ItemForCraft("gears", 5, 0),
new ItemForCraft("roadsigns", 5, 0),
new ItemForCraft("metal.fragments", 2000, 0)
}
},
new CraftConfig
{
Enabled = true,
ImageURL = "https://i.imgur.com/dmWQOm6.png",
Description = new List<string>
{
"Craft requires:",
"- Gears (5 pcs)",
"- Road Signs (5 pcs)",
"- Metal (2000 pcs)"
},
Command = "giverowboat",
Permission = "crafts.all",
DisplayName = "Row Boat",
ShortName = "coffin.storage",
Amount = 1,
SkinID = 2080150023,
Type = CraftType.Vehicle,
Prefab = "assets/content/vehicles/boats/rowboat/rowboat.prefab",
Level = WorkbenchLevel.Two,
UseDistance = true,
Distance = 1.5f,
GiveCommand = string.Empty,
Ground = true,
Structure = true,
Items = new List<ItemForCraft>
{
new ItemForCraft("gears", 5, 0),
new ItemForCraft("roadsigns", 5, 0),
new ItemForCraft("metal.fragments", 2000, 0)
}
},
new CraftConfig
{
Enabled = true,
ImageURL = "https://i.imgur.com/CgpVw2j.png",
Description = new List<string>
{
"Craft requires:",
"- Gears (5 pcs)",
"- Road Signs (5 pcs)",
"- Metal (2000 pcs)"
},
Command = "giverhibboat",
Permission = "crafts.all",
DisplayName = "RHIB",
ShortName = "electric.sirenlight",
Amount = 1,
SkinID = 2080150770,
Type = CraftType.Vehicle,
GiveCommand = string.Empty,
Prefab = "assets/content/vehicles/boats/rhib/rhib.prefab",
Level = WorkbenchLevel.Three,
UseDistance = true,
Distance = 1.5f,
Ground = true,
Structure = true,
Items = new List<ItemForCraft>
{
new ItemForCraft("gears", 5, 0),
new ItemForCraft("roadsigns", 5, 0),
new ItemForCraft("metal.fragments", 2000, 0)
}
},
new CraftConfig
{
Enabled = true,
ImageURL = "https://i.imgur.com/eioxlvK.png",
Description = new List<string>
{
"Craft requires:",
"- Gears (5 pcs)",
"- Road Signs (5 pcs)",
"- Metal (2000 pcs)"
},
Command = "givesedan",
Permission = "crafts.all",
DisplayName = "Car",
ShortName = "woodcross",
Amount = 1,
SkinID = 2080151780,
Type = CraftType.Vehicle,
GiveCommand = string.Empty,
Prefab = "assets/content/vehicles/sedan_a/sedantest.entity.prefab",
Level = WorkbenchLevel.Two,
UseDistance = true,
Distance = 1.5f,
Ground = true,
Structure = true,
Items = new List<ItemForCraft>
{
new ItemForCraft("gears", 5, 0),
new ItemForCraft("roadsigns", 5, 0),
new ItemForCraft("metal.fragments", 2000, 0)
}
},
new CraftConfig
{
Enabled = true,
ImageURL = "https://i.imgur.com/cp2Xx2A.png",
Description = new List<string>
{
"Craft requires:",
"- Gears (5 pcs)",
"- Road Signs (5 pcs)",
"- Metal (2000 pcs)"
},
Command = "givehotair",
Permission = "crafts.all",
DisplayName = "Hot Air Balloon",
ShortName = "box.repair.bench",
Amount = 1,
SkinID = 2080152635,
Type = CraftType.Vehicle,
GiveCommand = string.Empty,
Prefab = "assets/prefabs/deployable/hot air balloon/hotairballoon.prefab",
Level = WorkbenchLevel.Three,
UseDistance = true,
Distance = 1.5f,
Ground = true,
Structure = true,
Items = new List<ItemForCraft>
{
new ItemForCraft("gears", 5, 0),
new ItemForCraft("roadsigns", 5, 0),
new ItemForCraft("metal.fragments", 2000, 0)
}
},
new CraftConfig
{
Enabled = true,
ImageURL = "https://i.imgur.com/7JZE0Lr.png",
Description = new List<string>
{
"Craft requires:",
"- Gears (5 pcs)",
"- Road Signs (5 pcs)",
"- Metal (2000 pcs)"
},
Command = "givescrapheli",
Permission = "crafts.all",
DisplayName = "Transport Helicopter",
ShortName = "lantern",
Amount = 1,
SkinID = 2080154394,
Type = CraftType.Vehicle,
GiveCommand = string.Empty,
Prefab = "assets/content/vehicles/scrap heli carrier/scraptransporthelicopter.prefab",
Level = WorkbenchLevel.Three,
UseDistance = true,
Distance = 1.5f,
Ground = true,
Structure = true,
Items = new List<ItemForCraft>
{
new ItemForCraft("gears", 5, 0),
new ItemForCraft("roadsigns", 5, 0),
new ItemForCraft("metal.fragments", 2000, 0)
}
},
new CraftConfig
{
Enabled = true,
ImageURL = "https://i.imgur.com/LLB2AVi.png",
Description = new List<string>
{
"Craft requires:",
"- Gears (5 pcs)",
"- Road Signs (5 pcs)",
"- Metal (2000 pcs)"
},
Command = "giverecycler",
Permission = "crafts.all",
DisplayName = "Home Recycler",
ShortName = "research.table",
Amount = 1,
SkinID = 2186833264,
Type = CraftType.Recycler,
Prefab = "assets/bundled/prefabs/static/recycler_static.prefab",
GiveCommand = string.Empty,
Level = WorkbenchLevel.Two,
UseDistance = true,
Distance = 1.5f,
Ground = true,
Structure = true,
Items = new List<ItemForCraft>
{
new ItemForCraft("gears", 5, 0),
new ItemForCraft("roadsigns", 5, 0),
new ItemForCraft("metal.fragments", 2000, 0)
}
},
new CraftConfig
{
Enabled = true,
ImageURL = "https://i.imgur.com/mw1T17x.png",
Description = new List<string>
{
"Craft requires:",
"- Gears (5 pcs)",
"- Road Signs (5 pcs)",
"- Metal (2000 pcs)"
},
Command = "givelr300",
Permission = "crafts.all",
DisplayName = string.Empty,
ShortName = "rifle.lr300",
Amount = 1,
SkinID = 0,
Type = CraftType.Item,
Prefab = string.Empty,
GiveCommand = string.Empty,
Level = WorkbenchLevel.None,
UseDistance = true,
Distance = 1.5f,
Ground = true,
Structure = true,
Items = new List<ItemForCraft>
{
new ItemForCraft("gears", 5, 0),
new ItemForCraft("roadsigns", 5, 0),
new ItemForCraft("metal.fragments", 2000, 0)
}
},
new CraftConfig
{
Enabled = true,
ImageURL = "https://i.imgur.com/z7X5D5V.png",
Description = new List<string>
{
"Craft requires:",
"- Gears (5 pcs)",
"- Road Signs (5 pcs)",
"- Metal (2000 pcs)"
},
Command = "givemod1",
Permission = "crafts.all",
DisplayName = "Car",
ShortName = "electric.flasherlight",
Amount = 1,
SkinID = 2244308598,
Type = CraftType.ModularCar,
Prefab = string.Empty,
GiveCommand = string.Empty,
Level = WorkbenchLevel.Two,
UseDistance = true,
Distance = 1.5f,
Ground = true,
Structure = true,
Items = new List<ItemForCraft>
{
new ItemForCraft("gears", 5, 0),
new ItemForCraft("roadsigns", 5, 0),
new ItemForCraft("metal.fragments", 2000, 0)
},
Modular = new ModularCarConf
{
CodeLock = true,
KeyLock = false,
EnginePartsTier = 2,
FreshWaterAmount = 0,
FuelAmount = 140,
Modules = new[]
{
"vehicle.1mod.engine",
"vehicle.1mod.cockpit.armored",
"vehicle.1mod.cockpit.armored"
}
}
}
};
[JsonProperty(PropertyName = "Recycler Settings")]
public RecyclerConfig Recycler = new RecyclerConfig
{
Speed = 5f,
Radius = 7.5f,
Text = "<size=19>RECYCLER</size>\n<size=15>{0}/{1}</size>",
Color = "#C5D0E6",
Delay = 0.75f,
Available = true,
Owner = true,
Amounts = new[] {0.9f, 0, 0, 0, 0, 0.5f, 0, 0, 0, 0.9f, 0.5f, 0.5f, 0, 1, 1, 0.5f, 0, 0, 0, 0, 0, 1, 1},
Scale = 0.5f,
DDraw = true,
Building = true
};
[JsonProperty(PropertyName = "Car Settings")]
public CarConfig Car = new CarConfig
{
ActiveItems = new ActiveItemOptions
{
Disable = true,
BlackList = new[]
{
"explosive.timed", "rocket.launcher", "surveycharge", "explosive.satchel"
}
},
Radius = 7.5f,
Text = "<size=15>{0}/{1}</size>",
Color = "#C5D0E6",
Delay = 0.75f
};
}
private class CarConfig
{
[JsonProperty(PropertyName = "Active Items (in hand)")]
public ActiveItemOptions ActiveItems;
[JsonProperty(PropertyName = "DDraw Radius")]
public float Radius;
[JsonProperty(PropertyName = "DDraw Text")]
public string Text;
[JsonProperty(PropertyName = "DDraw Color")]
public string Color;
[JsonProperty(PropertyName = "DDraw Delay (sec)")]
public float Delay;
}
public class ActiveItemOptions
{
[JsonProperty(PropertyName = "Forbid to hold all items")]
public bool Disable;
[JsonProperty(PropertyName = "List of blocked items (shortname)",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public string[] BlackList;
}
private class RecyclerConfig
{
[JsonProperty(PropertyName = "Recycling speed")]
public float Speed;
[JsonProperty(PropertyName = "Use DDraw? (showing damage on the recycler)")]
public bool DDraw;
[JsonProperty(PropertyName = "DDraw Radius")]
public float Radius;
[JsonProperty(PropertyName = "DDraw Text")]
public string Text;
[JsonProperty(PropertyName = "DDraw Color")]
public string Color;
[JsonProperty(PropertyName = "DDraw Delay (sec)")]
public float Delay;
[JsonProperty(PropertyName = "Enabled pickup?")]
public bool Available;
[JsonProperty(PropertyName = "Only owner can pickup")]
public bool Owner;
[JsonProperty(PropertyName = "Check ability to build for pickup")]
public bool Building;
[JsonProperty(PropertyName = "BaseProtection Settings",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public float[] Amounts;
[JsonProperty(PropertyName = "Damage Scale")]
public float Scale;
}
private class WorkbenchConfig
{
[JsonProperty(PropertyName = "Color")] public string Color;
[JsonProperty(PropertyName = "Title")] public string Title;
public WorkbenchConfig(string color, string title)
{
Color = color;
Title = title;
}
}
private class CraftConfig
{
[JsonProperty(PropertyName = "Enabled craft?")]
public bool Enabled;
[JsonProperty(PropertyName = "Image")] public string ImageURL;
[JsonProperty(PropertyName = "Description")]
public List<string> Description;
[JsonProperty(PropertyName = "Command for give")]
public string Command;
[JsonProperty(PropertyName = "Permission for craft")]
public string Permission;
[JsonProperty(PropertyName = "DisplayName")]
public string DisplayName;
[JsonProperty(PropertyName = "Shortname")]
public string ShortName;
[JsonProperty(PropertyName = "Amount")]
public int Amount;
[JsonProperty(PropertyName = "Skin")] public ulong SkinID;
[JsonProperty(PropertyName = "Type (Item/Command/Vehicle/Recycler)")]
[JsonConverter(typeof(StringEnumConverter))]
public CraftType Type;
[JsonProperty(PropertyName = "Prefab (for Vehicle)")]
public string Prefab;
[JsonProperty(PropertyName = "Command on give")]
public string GiveCommand;
[JsonProperty(PropertyName = "Workbench Level")]
public WorkbenchLevel Level;
[JsonProperty(PropertyName = "Distance Check")]
public bool UseDistance;
[JsonProperty(PropertyName = "Distance")]
public float Distance;
[JsonProperty(PropertyName = "Place the ground")]
public bool Ground;
[JsonProperty(PropertyName = "Place the structure")]
public bool Structure;
[JsonProperty(PropertyName = "Items For Craft",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<ItemForCraft> Items;
[JsonProperty(PropertyName = "For Modular Car")]
public ModularCarConf Modular;
public Item ToItem()
{
var newItem = ItemManager.CreateByName(ShortName, Amount, SkinID);
if (newItem == null)
{
Debug.LogError($"Error creating item with ShortName: '{ShortName}'");
return null;
}
if (!DisplayName.IsNullOrEmpty()) newItem.name = DisplayName;
return newItem;
}
public void Give(BasePlayer player)
{
if (player == null) return;
var item = ToItem();
if (item == null) return;
player.GiveItem(item, BaseEntity.GiveItemReason.PickedUp);
}
public void Spawn(BasePlayer player, Vector3 pos, Quaternion rot)
{
switch (Type)
{
case CraftType.ModularCar:
{
_instance?.SpawnModularCar?.Call("API_SpawnPresetCar", player, Modular.Get());
break;
}
case CraftType.Vehicle:
{
var entity = GameManager.server.CreateEntity(Prefab, pos,
Quaternion.Euler(0, player.GetNetworkRotation().eulerAngles.y - 90, 0));
if (entity == null) return;
entity.skinID = SkinID;
entity.OwnerID = player.userID;
entity.Spawn();
break;
}
default:
{
var entity = GameManager.server.CreateEntity(Prefab, pos, rot);
if (entity == null) return;
entity.skinID = SkinID;
entity.OwnerID = player.userID;
entity.Spawn();
break;
}
}
}
}
private class ModularCarConf
{
[JsonProperty(PropertyName = "CodeLock")]
public bool CodeLock;
[JsonProperty(PropertyName = "KeyLock")]
public bool KeyLock;
[JsonProperty(PropertyName = "Engine Parts Tier")]
public int EnginePartsTier;
[JsonProperty(PropertyName = "Fresh Water Amount")]
public int FreshWaterAmount;
[JsonProperty(PropertyName = "Fuel Amount")]
public int FuelAmount;
[JsonProperty(PropertyName = "Modules", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public string[] Modules;
public Dictionary<string, object> Get()
{
return new Dictionary<string, object>
{
["CodeLock"] = CodeLock,
["KeyLock"] = KeyLock,
["EnginePartsTier"] = EnginePartsTier,
["FreshWaterAmount"] = FreshWaterAmount,
["FuelAmount"] = FuelAmount,
["Modules"] = Modules
};
}
}
private class ItemForCraft
{
[JsonProperty(PropertyName = "Shortname")]
public string ShortName;
[JsonProperty(PropertyName = "Amount")]
public int Amount;
[JsonProperty(PropertyName = "Skin")] public ulong SkinID;
public ItemForCraft(string shortname, int amount, ulong skin)
{
ShortName = shortname;
Amount = amount;
SkinID = skin;
}
}
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 OnServerInitialized(bool initialized)
{
_instance = this;
LoadImages();
if (_config.CraftsList.Exists(x => x.Enabled && x.Type == CraftType.ModularCar) && !SpawnModularCar)
PrintWarning("SpawnModularCar IS NOT INSTALLED.");
_config.CraftsList.ForEach(item =>
{
if (!string.IsNullOrEmpty(item.Command))
AddCovalenceCommand(item.Command, nameof(CmdGiveItem));
if (!string.IsNullOrEmpty(item.Permission) && !permission.PermissionExists(item.Permission))
permission.RegisterPermission(item.Permission, this);
});
if (initialized)
foreach (var ent in BaseNetworkable.serverEntities.OfType<BaseEntity>())
OnEntitySpawned(ent);
AddCovalenceCommand(_config.Command, nameof(CmdChatOpenUI));
}
private void Unload()
{
foreach (var player in BasePlayer.activePlayerList)
CuiHelper.DestroyUi(player, Layer);
foreach (var component in UnityEngine.Object.FindObjectsOfType<RecyclerComponent>())
if (component != null)
component.Kill();
foreach (var component in UnityEngine.Object.FindObjectsOfType<CarController>())
if (component != null)
component.Kill();
_config = null;
_instance = null;
}
private void OnEntityBuilt(Planner held, GameObject go)
{
if (held == null || go == null) return;
var player = held.GetOwnerPlayer();
if (player == null) return;
var entity = go.ToBaseEntity();
if (entity == null || entity.skinID == 0) return;
var craft = _config.CraftsList.Find(x =>
(x.Type == CraftType.Vehicle || x.Type == CraftType.Recycler || x.Type == CraftType.ModularCar) &&
x.SkinID == entity.skinID);
if (craft == null) return;
var transform = entity.transform;
var itemName = !string.IsNullOrEmpty(craft.DisplayName)
? craft.DisplayName
: ItemManager.FindItemDefinition(craft.ShortName)?.displayName.translated ?? "ITEM";
NextTick(() =>
{
if (entity != null)
entity.Kill();
});
RaycastHit rHit;
if (Physics.Raycast(transform.position + new Vector3(0, 0.1f, 0), Vector3.down, out rHit, 4f,
LayerMask.GetMask("Construction")) && rHit.GetEntity() != null)
{
if (!craft.Structure)
{
Reply(player, OnStruct, itemName);
GiveCraft(player, craft);
return;
}
}
else
{
if (!craft.Ground)
{
Reply(player, OnGround, itemName);
GiveCraft(player, craft);
return;
}
}
if (craft.UseDistance && Vector3.Distance(player.ServerPosition, transform.position) < craft.Distance)
{
Reply(player, BuildDistance, craft.Distance);
GiveCraft(player, craft);
return;
}
craft.Spawn(player, transform.position, transform.rotation);
}
private object CanResearchItem(BasePlayer player, Item item)
{
if (player == null || item == null ||
!_config.CraftsList.Exists(x => x.Type == CraftType.Vehicle && x.SkinID == item.skin)) return null;
return false;
}
private void OnEntitySpawned(BaseEntity entity)
{
if (entity == null) return;
if (entity is Recycler)
entity.gameObject.AddComponent<RecyclerComponent>();
if (entity is BasicCar)
entity.gameObject.AddComponent<CarController>();
}
private void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
{
if (entity == null || entity.OwnerID == 0) return;
var recycler = entity.GetComponent<RecyclerComponent>();
if (recycler != null)
{
info.damageTypes.ScaleAll(_config.Recycler.Scale);
recycler.DDraw();
}
var car = entity.GetComponent<CarController>();
if (car != null)
{
car.ManageDamage(info);
car.DDraw();
}
}
private object OnRecyclerToggle(Recycler recycler, BasePlayer player)
{
if (recycler == null || player == null) return null;
var component = recycler.GetComponent<RecyclerComponent>();
if (component == null) return null;
if (!recycler.IsOn())
{
foreach (var obj in recycler.inventory.itemList)
obj.CollectedForCrafting(player);
component.StartRecycling();
}
else
{
component.StopRecycling();
}
return false;
}
private void OnHammerHit(BasePlayer player, HitInfo info)
{
if (player == null || info == null) return;
var entity = info.HitEntity;
if (entity == null) return;
var component = entity.GetComponent<RecyclerComponent>();
if (component == null) return;
if (!_config.Recycler.Available)
{
Reply(player, NotTake);
return;
}
component.TryPickup(player);
}
#endregion
#region Commands
[ConsoleCommand("UI_Crafts")]
private void CmdConsoleCraft(ConsoleSystem.Arg arg)
{
var player = arg.Player();
if (player == null) return;
if (!arg.HasArgs())
{
DrawUI(player, isFirst: true);
return;
}
switch (arg.Args[0].ToLower())
{
case "page":
{
int page;
if (!arg.HasArgs(2) || !int.TryParse(arg.Args[1], out page)) return;
DrawUI(player, page);
break;
}
case "craft":
{
int itemid;
if (!arg.HasArgs(2) || !int.TryParse(arg.Args[1], out itemid)
|| !(itemid >= 0 && _config.CraftsList.Count > itemid)) return;
var craftItem = _config.CraftsList[itemid];
if (craftItem == null) return;
if (!HasWorkbench(player, craftItem.Level))
{
Reply(player, "NOT WORKBENCH");
return;
}
var playerItems = player.inventory.AllItems();
if (!HasAllItems(playerItems, craftItem))
{
Reply(player, "NOT.RESOURCES");
return;
}
craftItem.Items.ForEach(item =>
{
if (item != null)
Take(playerItems, item.ShortName, item.SkinID, item.Amount);
});
GiveCraft(player, craftItem);
//CraftItem(player, craftItem);
Reply(player, GIVECRAFT,
!string.IsNullOrEmpty(craftItem.DisplayName)
? craftItem.DisplayName
: ItemManager.FindItemDefinition(craftItem.ShortName).displayName.translated);
break;
}
}
}
private void CmdGiveItem(IPlayer iPlayer, string command, string[] args)
{
if (args.Length == 0) return;
var player = BasePlayer.Find(args[0]);
if (player == null)
{
Reply(iPlayer, "PLAYER NOT FOUND", args[0]);
return;
}
var craftItem = _config.CraftsList.Find(x => x.Command == command);
if (craftItem == null)
{
iPlayer.Reply("COMMAND NOT FOUND", command);
return;
}
var item = craftItem.ToItem();
if (item == null)
return;
var itemName = !string.IsNullOrEmpty(craftItem.DisplayName)
? craftItem.DisplayName
: item.info.displayName.translated;
player.GiveItem(item, BaseEntity.GiveItemReason.PickedUp);
Reply(player, GIVECRAFT, itemName);
if (_config.useDebug) Reply(iPlayer, "GIVE.DEBUG", player.displayName, player.UserIDString, itemName);
}
private void CmdChatOpenUI(IPlayer cov, string command, string[] args)
{
var player = cov?.Object as BasePlayer;
if (player == null) return;
DrawUI(player, isFirst: true);
}
#endregion
#region Interface
private void DrawUI(BasePlayer player, int page = 0, bool isFirst = false)
{
var container = new CuiElementContainer();
var playerItems = player.inventory.AllItems();
#region First
if (isFirst)
{
CuiHelper.DestroyUi(player, Layer);
#region BG
container.Add(new CuiPanel
{
RectTransform = {AnchorMin = "0 0", AnchorMax = "1 1"},
Image =
{
Color = "0.1 0.1 0.05 0.75",
Sprite = "assets/content/ui/ui.background.transparent.radial.psd",
Material = "assets/content/ui/uibackgroundblur-ingamemenu.mat"
},
CursorEnabled = true
}, "Overlay", Layer);
#endregion
#region Title
container.Add(new CuiLabel
{
RectTransform =
{AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-200 200", OffsetMax = "200 300"},
Text =
{
Text = lang.GetMessage("Title", this, player.UserIDString), Align = TextAnchor.MiddleCenter,
FontSize = 28
}
}, Layer);
#endregion
}
#endregion
#region Main
container.Add(new CuiPanel
{
RectTransform = {AnchorMin = "0 0", AnchorMax = "1 1"},
Image = {Color = "0 0 0 0"}
}, Layer, Layer + ".Main");
container.Add(new CuiButton
{
RectTransform =
{
AnchorMin = "1 1", AnchorMax = "1 1",
OffsetMin = "-45 -45", OffsetMax = "-5 -5"
},
Text =
{
Text = "✕",
Align = TextAnchor.MiddleCenter,
FontSize = 28,
Color = "1 1 1 1"
},
Button =
{
Color = "0 0 0 0",
Close = Layer
}
}, Layer + ".Main");
#region Items
var list = GetPlayerCrafts(player, page);
if (list.Count > 0)
{
var xSwitch = -(220 * list.Count + 40 * (list.Count - 1)) / 2;
for (var i = 0; i < list.Count; i++)
{
var craft = list[i];
container.Add(new CuiPanel
{
RectTransform =
{
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = $"{xSwitch} -100",
OffsetMax = $"{xSwitch + 220} 200"
},
Image = {Color = "0 0 0 0.5", Material = "assets/content/ui/uibackgroundblur-ingamemenu.mat"}
}, Layer + ".Main", Layer + $".Craft.{xSwitch}");
container.Add(new CuiElement
{
Parent = Layer + $".Craft.{xSwitch}",
Components =
{
new CuiRawImageComponent {Png = ImageLibrary.Call<string>("GetImage", craft.ImageURL)},
new CuiRectTransformComponent
{
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-70 0", OffsetMax = "70 140"
}
}
});
container.Add(new CuiPanel
{
RectTransform =
{
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-85 -10", OffsetMax = "85 -5"
},
Image = {Color = "1 1 1 1", Sprite = "assets/content/ui/gameui/compass/alpha_mask.png"}
}, Layer + $".Craft.{xSwitch}");
container.Add(new CuiLabel
{
RectTransform =
{AnchorMin = "0 0.5", AnchorMax = "1 0.5", OffsetMin = "0 -130", OffsetMax = "0 -25"},
Text =
{
Text = string.Join("\n", craft.Description), Align = TextAnchor.UpperCenter,
FontSize = 14
}
}, Layer + $".Craft.{xSwitch}");
container.Add(new CuiPanel
{
RectTransform =
{AnchorMin = "0 0", AnchorMax = "1 0", OffsetMin = "0 -50", OffsetMax = "0 -5"},
Image =
{
Color = HexToCuiColor(_config.Workbenchs[craft.Level].Color),
Material = "assets/content/ui/uibackgroundblur-ingamemenu.mat"
}
}, Layer + $".Craft.{xSwitch}", Layer + $".Craft.{xSwitch}.Workbench");
container.Add(new CuiLabel
{
RectTransform = {AnchorMin = "0 0", AnchorMax = "1 1"},
Text =
{
Text = _config.Workbenchs[craft.Level].Title, Align = TextAnchor.MiddleCenter,
FontSize = 14
}
}, Layer + $".Craft.{xSwitch}.Workbench");
var active = HasAllItems(playerItems, craft) && HasWorkbench(player, craft.Level);
container.Add(new CuiButton
{
RectTransform =
{AnchorMin = "0 0", AnchorMax = "1 0", OffsetMin = "0 -105", OffsetMax = "0 -55"},
Button =
{
Command = active ? $"UI_Crafts craft {_config.CraftsList.IndexOf(craft)}" : "",
Color = active ? HexToCuiColor(_config.GreenColor) : "0 0 0 0.5",
Material = "assets/content/ui/uibackgroundblur-ingamemenu.mat",
Close = active ? Layer : ""
},
Text =
{
Text = lang.GetMessage("Создать", this, player.UserIDString),
Align = TextAnchor.MiddleCenter, FontSize = 24
}
}, Layer + $".Craft.{xSwitch}");
xSwitch += 260;
}
}
else
{
container.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "0.5 0.5", AnchorMax = "0.5 0.5", OffsetMin = "-250 -150", OffsetMax = "250 150"
},
Text =
{
Text = lang.GetMessage("NOT CRAFTS", this, player.UserIDString),
Align = TextAnchor.MiddleCenter, FontSize = 34
}
}, Layer + ".Main");
}
#endregion
#region Pages
if (list.Count > 0)
{
container.Add(new CuiButton
{
RectTransform =
{AnchorMin = "0 0.5", AnchorMax = "0 0.5", OffsetMin = "5 -25", OffsetMax = "55 25"},
Button =
{
Command = page > 0 ? $"UI_Crafts page {page - 1}" : "",
Color = "0 0 0 0"
},
Text =
{
Text = "<", Align = TextAnchor.MiddleCenter, FontSize = 40,
Color = page > 0 ? "1 1 1 1" : "1 1 1 0.5"
}
}, Layer + ".Main");
var count = _config.CraftsList.Count(craft => craft.Enabled &&
(string.IsNullOrEmpty(craft.Permission) ||
permission.UserHasPermission(player.UserIDString,
craft.Permission)));
container.Add(new CuiButton
{
RectTransform =
{AnchorMin = "1 0.5", AnchorMax = "1 0.5", OffsetMin = "-55 -25", OffsetMax = "-5 25"},
Button =
{
Command = count > (page + 1) * 3 ? $"UI_Crafts page {page + 1}" : "",
Color = "0 0 0 0"
},
Text =
{
Text = ">", Align = TextAnchor.MiddleCenter, FontSize = 40,
Color = count > (page + 1) * 3 ? "1 1 1 1" : "1 1 1 0.5"
}
}, Layer + ".Main");
}
#endregion
#endregion
CuiHelper.DestroyUi(player, Layer + ".Main");
CuiHelper.AddUi(player, container);
}
#endregion
#region Utils
private void GiveCraft(BasePlayer player, CraftConfig cfg)
{
switch (cfg.Type)
{
case CraftType.Command:
{
var command = cfg.GiveCommand.Replace("\n", "|").Replace("%steamid%", player.UserIDString, StringComparison.OrdinalIgnoreCase).Replace(
"%username%",
player.displayName, StringComparison.OrdinalIgnoreCase);
foreach (var check in command.Split('|')) Server.Command(check);
break;
}
default:
{
cfg.Give(player);
break;
}
}
}
private void CraftItem(BasePlayer player, CraftConfig item)
{
var defenition = ItemManager.FindItemDefinition(item.ShortName);
var task = Pool.Get<ItemCraftTask>();
task.blueprint = defenition.Blueprint;
task.endTime = 0.0f;
task.taskUID = player.inventory.crafting.taskUID + 1;
task.owner = player;
task.instanceData = null;
if (task.instanceData != null)
task.instanceData.ShouldPool = false;
task.amount = 1;
task.skinID = (int) item.SkinID;
player.inventory.crafting.queue.AddLast(task);
if (task.owner != null)
task.owner.Command("note.craft_add", task.taskUID, task.blueprint.targetItem.itemid,
1, task.skinID);
}
private static bool HasWorkbench(BasePlayer player, WorkbenchLevel level)
{
return level == WorkbenchLevel.Three ? player.HasPlayerFlag(BasePlayer.PlayerFlags.Workbench3)
: level == WorkbenchLevel.Two ? player.HasPlayerFlag(BasePlayer.PlayerFlags.Workbench3) ||
player.HasPlayerFlag(BasePlayer.PlayerFlags.Workbench2)
: level == WorkbenchLevel.One ? player.HasPlayerFlag(BasePlayer.PlayerFlags.Workbench3) ||
player.HasPlayerFlag(BasePlayer.PlayerFlags.Workbench2) ||
player.HasPlayerFlag(BasePlayer.PlayerFlags.Workbench1)
: level == WorkbenchLevel.None;
}
private static bool HasAllItems(IReadOnlyList<Item> items, CraftConfig craftConfig)
{
for (var i = 0; i < craftConfig.Items.Count; i++)
{
var itemForCraft = craftConfig.Items[i];
if (ItemCount(items, itemForCraft.ShortName, itemForCraft.SkinID) < itemForCraft.Amount) return false;
}
return true;
}
private static int ItemCount(IReadOnlyList<Item> items, string shortname, ulong skin)
{
var result = 0;
for (var i = 0; i < items.Count; i++)
{
var item = items[i];
if (item.info.shortname == shortname && (skin == 0 || item.skin == skin))
result += item.amount;
}
return result;
}
private void Take(IEnumerable<Item> itemList, string shortname, ulong skinId, int iAmount)
{
var num1 = 0;
if (iAmount == 0) return;
var list = Pool.GetList<Item>();
foreach (var item in itemList)
{
if (item.info.shortname != shortname ||
skinId != 0 && item.skin != skinId) continue;
var num2 = iAmount - num1;
if (num2 <= 0) continue;
if (item.amount > num2)
{
item.MarkDirty();
item.amount -= num2;
num1 += num2;
break;
}
if (item.amount <= num2)
{
num1 += item.amount;
list.Add(item);
}
if (num1 == iAmount)
break;
}
foreach (var obj in list)
obj.RemoveFromContainer();
Pool.FreeList(ref list);
}
private static string HexToCuiColor(string hex)
{
if (string.IsNullOrEmpty(hex)) hex = "#FFFFFFFF";
var str = hex.Trim('#');
if (str.Length == 6)
str += "FF";
if (str.Length != 8) throw new Exception(hex);
var r = byte.Parse(str.Substring(0, 2), NumberStyles.HexNumber);
var g = byte.Parse(str.Substring(2, 2), NumberStyles.HexNumber);
var b = byte.Parse(str.Substring(4, 2), NumberStyles.HexNumber);
var a = byte.Parse(str.Substring(6, 2), NumberStyles.HexNumber);
Color color = new Color32(r, g, b, a);
return $"{color.r:F2} {color.g:F2} {color.b:F2} {color.a:F2}";
}
private List<CraftConfig> GetPlayerCrafts(BasePlayer player, int page, int count = 3)
{
return _config.CraftsList.FindAll(craft => craft.Enabled && (string.IsNullOrEmpty(craft.Permission) ||
permission.UserHasPermission(player.UserIDString,
craft.Permission))).Skip(page * count).Take(count).ToList();
}
private static Color HexToUnityColor(string hex)
{
if (string.IsNullOrEmpty(hex)) hex = "#FFFFFFFF";
var str = hex.Trim('#');
if (str.Length == 6)
str += "FF";
if (str.Length != 8) throw new Exception(hex);
var r = byte.Parse(str.Substring(0, 2), NumberStyles.HexNumber);
var g = byte.Parse(str.Substring(2, 2), NumberStyles.HexNumber);
var b = byte.Parse(str.Substring(4, 2), NumberStyles.HexNumber);
var a = byte.Parse(str.Substring(6, 2), NumberStyles.HexNumber);
Color color = new Color32(r, g, b, a);
return color;
}
private static void SetPlayerFlag(BasePlayer player, BasePlayer.PlayerFlags f, bool b)
{
if (b)
{
if (player.HasPlayerFlag(f)) return;
player.playerFlags |= f;
}
else
{
if (!player.HasPlayerFlag(f)) return;
player.playerFlags &= ~f;
}
player.SendNetworkUpdateImmediate();
}
private void LoadImages()
{
timer.In(5f, () =>
{
if (!ImageLibrary)
{
PrintWarning("IMAGE LIBRARY IS NOT INSTALLED.");
}
else
{
var imagesList = new Dictionary<string, string>();
_config.CraftsList.ForEach(item =>
{
if (!string.IsNullOrEmpty(item.ImageURL) && !imagesList.ContainsKey(item.ImageURL))
imagesList.Add(item.ImageURL, item.ImageURL);
});
ImageLibrary?.Call("ImportImageList", Title, imagesList, 0UL, true);
}
});
}
#endregion
#region Recycler Component
private class RecyclerComponent : FacepunchBehaviour
{
private Recycler recycler;
private GroundWatch groundWatch;
private DestroyOnGroundMissing groundMissing;
[NonSerialized] private readonly BaseEntity[] SensesResults = new BaseEntity[64];
private void Awake()
{
recycler = GetComponent<Recycler>();
if (recycler.OwnerID != 0)
{
recycler.baseProtection = ScriptableObject.CreateInstance<ProtectionProperties>();
recycler.baseProtection.amounts = _config.Recycler.Amounts;
groundWatch = recycler.GetOrAddComponent<GroundWatch>();
groundMissing = recycler.GetOrAddComponent<DestroyOnGroundMissing>();
}
}
public void DDraw()
{
if (recycler == null)
{
Kill();
return;
}
if (recycler.OwnerID == 0 || !_config.Recycler.DDraw)
return;
var inSphere = BaseEntity.Query.Server.GetInSphere(recycler.transform.position, _config.Recycler.Radius,
SensesResults, entity => entity is BasePlayer);
if (inSphere == 0)
return;
for (var i = 0; i < inSphere; i++)
{
var user = SensesResults[i] as BasePlayer;
if (user == null || user.IsDestroyed || !user.IsConnected || user.IsNpc ||
!user.userID.IsSteamId()) continue;
if (user.Connection.authLevel < 2) SetPlayerFlag(user, BasePlayer.PlayerFlags.IsAdmin, true);
user.SendConsoleCommand("ddraw.text", _config.Recycler.Delay,
HexToUnityColor(_config.Recycler.Color),
recycler.transform.position + Vector3.up,
string.Format(_config.Recycler.Text, recycler.health, recycler._maxHealth));
if (user.Connection.authLevel < 2) SetPlayerFlag(user, BasePlayer.PlayerFlags.IsAdmin, false);
}
}
#region Methods
public void StartRecycling()
{
if (recycler.IsOn())
return;
InvokeRepeating(RecycleThink, _config.Recycler.Speed, _config.Recycler.Speed);
Effect.server.Run(recycler.startSound.resourcePath, recycler, 0U, Vector3.zero, Vector3.zero);
recycler.SetFlag(BaseEntity.Flags.On, true);
recycler.SendNetworkUpdateImmediate();
}
public void StopRecycling()
{
CancelInvoke(RecycleThink);
if (!recycler.IsOn())
return;
Effect.server.Run(recycler.stopSound.resourcePath, recycler, 0U, Vector3.zero, Vector3.zero);
recycler.SetFlag(BaseEntity.Flags.On, false);
recycler.SendNetworkUpdateImmediate();
}
public void RecycleThink()
{
var flag = false;
var num1 = recycler.recycleEfficiency;
for (var slot1 = 0; slot1 < 6; ++slot1)
{
var slot2 = recycler.inventory.GetSlot(slot1);
if (slot2 != null)
{
if (Interface.CallHook("OnRecycleItem", recycler, slot2) != null)
{
if (HasRecyclable())
return;
StopRecycling();
return;
}
if (slot2.info.Blueprint != null)
{
if (slot2.hasCondition)
num1 = Mathf.Clamp01(
num1 * Mathf.Clamp(slot2.conditionNormalized * slot2.maxConditionNormalized, 0.1f,
1f));
var num2 = 1;
if (slot2.amount > 1)
num2 = Mathf.CeilToInt(Mathf.Min(slot2.amount, slot2.info.stackable * 0.1f));
if (slot2.info.Blueprint.scrapFromRecycle > 0)
{
var iAmount = slot2.info.Blueprint.scrapFromRecycle * num2;
if (slot2.info.stackable == 1 && slot2.hasCondition)
iAmount = Mathf.CeilToInt(iAmount * slot2.conditionNormalized);
if (iAmount >= 1)
recycler.MoveItemToOutput(ItemManager.CreateByName("scrap", iAmount));
}
if (!string.IsNullOrEmpty(slot2.info.Blueprint.RecycleStat))
{
var list = Pool.GetList<BasePlayer>();
Vis.Entities(transform.position, 3f, list, 131072);
foreach (var basePlayer in list)
if (basePlayer.IsAlive() && !basePlayer.IsSleeping() &&
basePlayer.inventory.loot.entitySource == recycler)
{
basePlayer.stats.Add(slot2.info.Blueprint.RecycleStat, num2,
Stats.Steam | Stats.Life);
basePlayer.stats.Save();
}
Pool.FreeList(ref list);
}
slot2.UseItem(num2);
using (var enumerator = slot2.info.Blueprint.ingredients.GetEnumerator())
{
while (enumerator.MoveNext())
{
var current = enumerator.Current;
if (current != null && current.itemDef.shortname != "scrap")
{
var num3 = current.amount / slot2.info.Blueprint.amountToCreate;
var num4 = 0;
if (num3 <= 1.0)
{
for (var index = 0; index < num2; ++index)
if (Random.Range(0.0f, 1f) <= num3 * (double) num1)
++num4;
}
else
{
num4 = Mathf.CeilToInt(
Mathf.Clamp(num3 * num1 * Random.Range(1f, 1f), 0.0f, current.amount) *
num2);
}
if (num4 > 0)
{
var num5 = Mathf.CeilToInt(num4 / (float) current.itemDef.stackable);
for (var index = 0; index < num5; ++index)
{
var iAmount = num4 > current.itemDef.stackable
? current.itemDef.stackable
: num4;
if (!recycler.MoveItemToOutput(ItemManager.Create(current.itemDef,
iAmount)))
flag = true;
num4 -= iAmount;
if (num4 <= 0)
break;
}
}
}
}
break;
}
}
}
}
if (!flag && HasRecyclable())
return;
StopRecycling();
}
public bool HasRecyclable()
{
for (var slot1 = 0; slot1 < 6; ++slot1)
{
var slot2 = recycler.inventory.GetSlot(slot1);
if (slot2 != null)
{
var can = Interface.CallHook("CanRecycle", recycler, slot2);
if (can is bool)
return (bool) can;
if (slot2.info.Blueprint != null)
return true;
}
}
return false;
}
#endregion
#region Destroy
public void TryPickup(BasePlayer player)
{
if (_config.Recycler.Building && !player.CanBuild())
{
_instance.Reply(player, CantBuild);
return;
}
if (_config.Recycler.Owner && recycler.OwnerID != player.userID)
{
_instance.Reply(player, OnlyOwner);
return;
}
if (recycler.SecondsSinceDealtDamage < 30f)
{
_instance.Reply(player, RecentlyDamaged);
return;
}
recycler.Kill();
var craft = _config.CraftsList.Find(x => x.Type == CraftType.Recycler);
if (craft == null)
{
_instance.Reply(player, CannotGive);
return;
}
_instance?.GiveCraft(player, craft);
}
private void OnDestroy()
{
CancelInvoke();
Destroy(this);
}
public void Kill()
{
Destroy(this);
}
#endregion
}
#endregion
#region Car Component
public class CarController : FacepunchBehaviour
{
public BasicCar entity;
public BasePlayer player;
public bool isDieing;
private bool allowHeldItems;
private string[] disallowedItems;
[NonSerialized] private readonly BaseEntity[] SensesResults = new BaseEntity[64];
private void Awake()
{
entity = GetComponent<BasicCar>();
allowHeldItems = !_config.Car.ActiveItems.Disable;
disallowedItems = _config.Car.ActiveItems.BlackList;
}
private void Update()
{
UpdateHeldItems();
CheckWaterLevel();
}
public void ManageDamage(HitInfo info)
{
if (isDieing)
{
NullifyDamage(info);
return;
}
if (info.damageTypes.GetMajorityDamageType() == DamageType.Bullet)
info.damageTypes.ScaleAll(200);
if (info.damageTypes.Total() >= entity.health)
{
isDieing = true;
NullifyDamage(info);
OnDeath();
}
}
public void DDraw()
{
if (entity == null)
{
Kill();
return;
}
if (entity.OwnerID == 0)
return;
var inSphere = BaseEntity.Query.Server.GetInSphere(entity.transform.position, _config.Car.Radius,
SensesResults, ent => ent is BasePlayer);
if (inSphere == 0)
return;
for (var i = 0; i < inSphere; i++)
{
var user = SensesResults[i] as BasePlayer;
if (user == null || user.IsDestroyed || !user.IsConnected || user.IsNpc ||
!user.userID.IsSteamId()) continue;
if (user.Connection.authLevel < 2) SetPlayerFlag(user, BasePlayer.PlayerFlags.IsAdmin, true);
user.SendConsoleCommand("ddraw.text", _config.Car.Delay, HexToUnityColor(_config.Car.Color),
entity.transform.position + new Vector3(0.25f, 1, 0),
string.Format(_config.Car.Text, entity.health, entity._maxHealth));
if (user.Connection.authLevel < 2) SetPlayerFlag(user, BasePlayer.PlayerFlags.IsAdmin, false);
}
}
private void NullifyDamage(HitInfo info)
{
info.damageTypes = new DamageTypeList();
info.HitEntity = null;
info.HitMaterial = 0;
info.PointStart = Vector3.zero;
}
public void UpdateHeldItems()
{
if (player == null)
return;
var item = player.GetActiveItem();
if (item == null || item.GetHeldEntity() == null)
return;
if (disallowedItems.Contains(item.info.shortname) || !allowHeldItems)
{
_instance?.Reply(player, ItemNotAllowed);
var slot = item.position;
item.SetParent(null);
item.MarkDirty();
Invoke(() =>
{
if (player == null) return;
item.SetParent(player.inventory.containerBelt);
item.position = slot;
item.MarkDirty();
}, 0.15f);
}
}
public void CheckWaterLevel()
{
if (WaterLevel.Factor(entity.WorldSpaceBounds().ToBounds()) > 0.7f)
StopToDie();
}
public void StopToDie(bool death = true)
{
if (entity != null)
{
entity.SetFlag(BaseEntity.Flags.Reserved1, false);
foreach (var wheel in entity.wheels)
{
wheel.wheelCollider.motorTorque = 0;
wheel.wheelCollider.brakeTorque = float.MaxValue;
}
entity.GetComponent<Rigidbody>().velocity = Vector3.zero;
if (player != null)
entity.DismountPlayer(player);
}
if (death) OnDeath();
}
private void OnDeath()
{
isDieing = true;
if (player != null)
player.EnsureDismounted();
Invoke(() =>
{
Effect.server.Run("assets/prefabs/npc/patrol helicopter/effects/heli_explosion.prefab",
transform.position);
_instance.NextTick(() =>
{
if (entity != null && !entity.IsDestroyed)
entity.DieInstantly();
Destroy(this);
});
}, 5f);
}
public void Kill()
{
StopToDie(false);
Destroy(this);
}
}
#endregion
#region Lang
private const string
NOTRESOURCES = "NOT.RESOURCES",
PLAYERNOTFOUND = "PLAYER NOT FOUND",
COMMANDNOTFOUND = "COMMAND NOT FOUND",
GIVECRAFT = "GIVECRAFT",
GIVEDEBUG = "GIVE.DEBUG",
NOTWORKBENCH = "NOT WORKBENCH",
NOTCRAFTS = "NOT CRAFTS",
CREATE = "CREATE",
TitleMain = "Title",
OnGround = "OnGround",
BuildDistance = "BuildDistance",
OnStruct = "OnStruct",
NotTake = "NotTake",
ItemN
// ... [Code preview truncated due to length. Full source in attached file] ...