C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Epic.OnlineServices;
using JSON;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using static BaseEntity;
using static ConsoleSystem;
using static Planner;
namespace Oxide.Plugins {
[Info("GridPower", "Nikedemos", "1.0.25")]
[Description("Allows players to take over, maintain and upgrade powerline poles to draw electric power")]
public class GridPower : RustPlugin {
#region CONST & STATIC
public const string FX_PLUG = "assets/prefabs/tools/wire/effects/plugeffect.prefab";
public const string FX_TECHTRASH = "assets/prefabs/deployable/dropbox/effects/submit_items.prefab";
public const string FX_COMBINER = "assets/prefabs/deployable/dropbox/effects/dropbox-deploy.prefab";
public const string FX_SELECT = "assets/bundled/prefabs/fx/notice/item.select.fx.prefab";
public const string ITEM_FUSE = "fuse";
public const string ITEM_TECHTRASH = "techparts";
public const string ITEM_ROOTCOMBINER = "electrical.combiner";
public const string ITEM_LADDER = "ladder.wooden.wall";
public const string ITEM_HAZMAT_SUIT = "hazmatsuit";
public const string ITEM_NOMAD_SUIT = "hazmatsuit.nomadsuit";
public const string ITEM_SPACESUIT = "hazmatsuit.spacesuit";
public const string ITEM_SCIENTIST_SUIT = "hazmatsuit_scientist";
public const string ITEM_SCIENTIST_SUIT_HEAVY = "scientistsuit_heavy";
public const string ITEM_SCIENTIST_SUIT_PEACEKEEPER = "hazmatsuit_scientist_peacekeeper";
public const string ITEM_FROGBOOTS = "boots.frog";
public const string ITEM_HOODIE = "hoodie";
public const string ITEM_PANTS = "pants";
public const ulong SKIN_HOODIE_COBALT_ELECTRIC = 1581890527;
public const ulong SKIN_HOODIE_COBALT_ELECTRIC_NOGLOW = 1582492745;
public const ulong SKIN_PANTS_COBALT_ELECTRIC = 1581896222;
public const uint PREFAB_FLOOR_ID = 916411076;
public const string PREFAB_POWERLINE_POLE_LIGHTS_1 = "assets/bundled/prefabs/autospawn/decor/powerline-small/powerline_pole_a.prefab";
public const string PREFAB_POWERLINE_POLE_LIGHTS_2 = "assets/content/props/powerline_poles/powerline_pole_a.prefab";
public const string PREFAB_LADDER = "assets/prefabs/building/ladder.wall.wood/ladder.wooden.wall.prefab";
public const string PREFAB_FUSEBOX = "assets/prefabs/io/electric/switches/fusebox/fusebox.prefab";
public const string PREFAB_BUTTON = "assets/prefabs/deployable/playerioents/button/button.prefab";
public const string PREFAB_ROOT_COMBINER = "assets/prefabs/deployable/playerioents/gates/combiner/electrical.combiner.deployed.prefab";
public const string PREFAB_TESLA = "assets/prefabs/deployable/playerioents/teslacoil/teslacoil.deployed.prefab";
public const string PREFAB_SPHERE = "assets/prefabs/visualization/sphere.prefab";
public const string PREFAB_GENERATOR_NEW = "assets/prefabs/deployable/playerioents/generators/generator.small.prefab";
public const string PREFAB_GENERATOR_OLD = "assets/prefabs/io/electric/generators/generator.static.prefab";
public const string PREFAB_SPOTLIGHT = "assets/bundled/prefabs/modding/cinematic/cinelights/cinelight_spot_warm.prefab";
public const string PREFAB_POINTLIGHT = "assets/bundled/prefabs/modding/cinematic/cinelights/cinelight_point_warm.prefab";
public const string COLLIDER_NAME_STREETLAMP_RIGHT = "powerline_pole_light (2)";
public const string COLLIDER_NAME_STREETLAMP_LEFT = "powerline_pole_light (3)";
//leave this alone or else you might break vanilla fuses
//public const float NORMAL_FUSE_DURATION_SECONDS = 200F;
public const string CMD_GP_CFG = "gp_cfg";
public const string CMD_GP_POLE = "gp_pole";
public const string CMD_GP_EMERGENCY_CLEANUP = "gp_emergency_cleanup";
public static BaseEntity.Flags FLAG_BAKED = BaseEntity.Flags.Reserved11;
public static BaseEntity.Flags FLAG_INTERNAL = BaseEntity.Flags.Reserved10;
public static BaseEntity.Flags FLAG_INDESTRUCTIBLE = BaseEntity.Flags.Reserved9;
public static BaseEntity.Flags FLAG_HIDE_MESH = BaseEntity.Flags.Reserved1;
private static GridPower Instance;
private static bool Unloading = false;
private static bool EmergencyCleanup = false;
//public static ListHashSet<string> ValidTools;
public static ItemDefinition[] FuseboxAllowedItems;
public static DeployVolume[] MonumentTestingDeployVolumes;
public const uint LADDER_PREFAB_ID = 2150203378;
public static Construction LadderConstruction;
public static GameObject ThePowerGridGameObject;
public const int BREAK_TOASTS_EVERY_N_CHARACTERS = 80;
#endregion
#region PERMISSIONS
public const string PERM_ADMIN = "gridpower.admin";
public const string PERM_VIP1 = "gridpower.vip1";
public const string PERM_VIP2 = "gridpower.vip2";
public const string PERM_VIP3 = "gridpower.vip3";
public const string PERM_VIP4 = "gridpower.vip4";
public const string PERM_VIP5 = "gridpower.vip5";
#endregion
#region CONFIG
public enum VersionComparisonResult {
Equal, PreviousIsOlder, PreviousIsNewer
}
public VersionComparisonResult CompareVersions(string previousVersion, string currentVersion, out Tuple<int, int, int> tuplePrevious, out Tuple<int, int, int> tupleCurrent) {
tuplePrevious = null;
tupleCurrent = null;
if (previousVersion == currentVersion) {
return VersionComparisonResult.Equal;
}
var previousVersionSeparated = previousVersion.Split('.');
var currentVersionSeparated = currentVersion.Split('.');
int previousVersionValue = 0;
int currentVersionValue = 0;
int multiplier = 1;
int previousMajor = 0;
int previousMinor = 0;
int previousBuild = 0;
int currentMajor = 0;
int currentMinor = 0;
int currentBuild = 0;
for (var i = 2; i >= 0; i--) {
int previousVersionPart = int.Parse(previousVersionSeparated[i]);
int currentVersionPart = int.Parse(currentVersionSeparated[i]);
switch (i) {
case 2 : {
previousBuild = previousVersionPart;
currentBuild = currentVersionPart;
}
break;
case 1 : {
previousMinor = previousVersionPart;
currentMinor = currentVersionPart;
}
break;
case 0 : {
previousMajor = previousVersionPart;
currentMajor = currentVersionPart;
}
break;
}
previousVersionValue += previousVersionPart * multiplier;
currentVersionValue += currentVersionPart * multiplier;
multiplier *= 1024;
}
tuplePrevious = new Tuple<int, int, int>(previousMajor, previousMinor, previousBuild);
tupleCurrent = new Tuple<int, int, int>(currentMajor, currentMinor, currentBuild);
if (previousVersionValue < currentVersionValue) {
return VersionComparisonResult.PreviousIsOlder;
}
return VersionComparisonResult.PreviousIsNewer;
}
private ConfigData Configuration;
public class ConfigData {
public string Version = "1.0.25";
public Dictionary<string, PermissionProfile> PermissionProfiles = new Dictionary<string, PermissionProfile>();
public Dictionary<string, List<ulong>> ProtectiveClothing = new Dictionary<string, List<ulong>>();
public float GeneratorChancePowerlineFunctional = 9.33F;
public int GeneratorInitialOutletsMin = 0;
public int GeneratorInitialOutletsMax = 4;
public int GeneratorInitialLevelMin = 1;
public int GeneratorInitialLevelMax = 5;
public float GridProductionStartAtHour = 8F;
public float GridProductionEndAtHour = 20F;
public float StreetlightsTurnOnAtHour = 20F;
public float StreetlightsTurnOffAtHour = 8F;
public bool StreetlightsEnable = true;
public float StreetlightsReliability = 0.75F;
public float StreetlightsFlickerLengthMin = 0.1F;
public float StreetlightsFlickerLengthMax = 1F;
public bool GridConstantPower = false;
public bool StreetlightsConstantPower = false;
public int PowerlinePowerPerTechTrash = 5;
public float PowerlineFuseDurationSeconds = 12000F;
public int PowerlineMaxTechTrashLevel = 50;
public bool FuseRequired = true;
public bool BuildingBlockPreventsButtonPress = true;
public bool BuildingBlockPreventsRootCombinerDamage = false;
//new in 1.0.16
public bool BuildingBlockPreventsRootCombinerPickup = false;
public bool BuildingBlockPreventsWireManipulation = true;
public bool BuildingBlockPrioritiseCloserIfTrueOlderIfFalse = false;
public string UpgradeItemShortname = ITEM_TECHTRASH;
//
public ulong UpgradeItemSkinID = 0;
public string UpgradeItemGuiName = string.Empty;
//new in 1.0.16
public bool PlayerMessagesAsToasts = true;
//new in 1.0.17
public string FuseItemShortname = ITEM_FUSE;
public ulong FuseItemSkinID = 0;
public string FuseItemGuiName = string.Empty;
//new in 1.0.25
public bool AutoLaddersEnabled = false;
/*
* public static Vector3 LADDER_BOTTOM_POSITION = new Vector3(0F, 4F, 0.15F);
public static Vector3 LADDER_BOTTOM_ROTATION = Vector3.zero;
public static Vector3 LADDER_TOP_POSITION = new Vector3(0F, 8.25F, 0.15F);
public static Vector3 LADDER_TOP_ROTATION = Vector3.zero;
*/
public float AutoLadderBottomPosX = 0F;
public float AutoLadderBottomPosY = 3.75F;
public float AutoLadderBottomPosZ = 0.15F;
public float AutoLadderBottomRotX = 0F;
public float AutoLadderBottomRotY = 0F;
public float AutoLadderBottomRotZ = 0F;
public float AutoLadderTopPosX = 0F;
public float AutoLadderTopPosY = 8F;
public float AutoLadderTopPosZ = 0.15F;
public float AutoLadderTopRotX = 0F;
public float AutoLadderTopRotY = 0F;
public float AutoLadderTopRotZ = 0F;
[JsonIgnore]
public Vector3 AutoLadderBottomPos;
[JsonIgnore]
public Vector3 AutoLadderBottomRot;
[JsonIgnore]
public Vector3 AutoLadderTopPos;
[JsonIgnore]
public Vector3 AutoLadderTopRot;
}
protected override void LoadDefaultConfig() {
RestoreDefaultConfig();
}
private void ProcessConfigData(out string oldVersion) {
bool needsSave = false;
oldVersion = Configuration.Version;
if (Configuration.PermissionProfiles?.Count == 0) {
Instance.PrintWarning("No permission profiles (yet?), generating default...");
Configuration.PermissionProfiles = new Dictionary<string, PermissionProfile> {
[PERM_ADMIN]
= AdminPermissionProfile(), ["default"] = DefaultPermissionProfile(), [PERM_VIP1] = VIPPermissionProfile(),
}
;
needsSave = true;
}
if (Configuration.ProtectiveClothing?.Count == 0) {
Configuration.ProtectiveClothing = new Dictionary<string, List<ulong>> {
//hazzie
[ITEM_HAZMAT_SUIT]
= null, [ITEM_SCIENTIST_SUIT_HEAVY] = null, [ITEM_SCIENTIST_SUIT] = null, [ITEM_SCIENTIST_SUIT_PEACEKEEPER] = null, [ITEM_SPACESUIT] = null, [ITEM_FROGBOOTS] = null, [ITEM_NOMAD_SUIT] = null, [ITEM_PANTS] = new List<ulong> {
SKIN_PANTS_COBALT_ELECTRIC
}
, [ITEM_HOODIE] = new List<ulong> {
SKIN_HOODIE_COBALT_ELECTRIC, SKIN_HOODIE_COBALT_ELECTRIC_NOGLOW
}
}
;
needsSave = true;
}
string versionFromConfig = Configuration.Version;
if (CompareVersions(versionFromConfig, "1.0.20", out _, out _) == VersionComparisonResult.PreviousIsOlder) {
Instance.PrintWarning($"\n\nYou have succesfully updated from {Configuration.Version} to 1.0.20\n");
Configuration.Version = "1.0.20";
needsSave = true;
}
if (CompareVersions(versionFromConfig, "1.0.24", out _, out _) == VersionComparisonResult.PreviousIsOlder) {
Instance.PrintWarning($"\n\nYou have succesfully updated from {Configuration.Version} to 1.0.24\n");
Configuration.Version = "1.0.24";
needsSave = true;
}
if (CompareVersions(versionFromConfig, "1.0.25", out _, out _) == VersionComparisonResult.PreviousIsOlder) {
Instance.PrintWarning($"\n\nYou have succesfully updated from {Configuration.Version} to 1.0.25\n");
Configuration.Version = "1.0.25";
needsSave = true;
}
Instance.Configuration.AutoLadderBottomPos = new Vector3(Instance.Configuration.AutoLadderBottomPosX, Instance.Configuration.AutoLadderBottomPosY, Instance.Configuration.AutoLadderBottomPosZ);
Instance.Configuration.AutoLadderBottomRot = new Vector3(Instance.Configuration.AutoLadderBottomRotX, Instance.Configuration.AutoLadderBottomRotY, Instance.Configuration.AutoLadderBottomRotZ);
Instance.Configuration.AutoLadderTopPos = new Vector3(Instance.Configuration.AutoLadderTopPosX, Instance.Configuration.AutoLadderTopPosY, Instance.Configuration.AutoLadderTopPosZ);
Instance.Configuration.AutoLadderTopRot = new Vector3(Instance.Configuration.AutoLadderTopRotX, Instance.Configuration.AutoLadderTopRotY, Instance.Configuration.AutoLadderTopRotZ);
if (needsSave) {
SaveConfigData();
}
}
private void RestoreDefaultConfig() {
PrintWarning("Generating default config...");
Configuration = new ConfigData();
SaveConfigData();
}
private void LoadConfigData() {
PrintWarning("Loading configuration file...");
try {
Configuration = Config.ReadObject<ConfigData>();
PrintWarning("Success.");
}
catch (Exception e) {
Configuration = new ConfigData();
PrintWarning($"Loading failed: \n{e.Message}\n{e.StackTrace}\nGenerating new config file...");
}
SaveConfigData();
}
private void SaveConfigData() {
PrintWarning("Saving config...");
Config.WriteObject(Configuration, true);
}
public class InteractiveConfigValue {
private Func<object> getter;
private Action<object> setter;
private Func<object, bool> validator;
public Func<object, string> formatter;
private Func<object, object> parser;
public Type valueType;
private double lowerLimit;
private double upperLimit;
//name also identifies it in the dic
public string name;
public string description;
public InteractiveConfigValue(string name, string description, Func<object> getter, Action<object> setter, Type validatorType = null, double lowerLimit = double.NegativeInfinity, double upperLimit = double.PositiveInfinity) {
this.getter = getter;
this.setter = setter;
this.name = name;
this.description = description;
this.lowerLimit = lowerLimit;
this.upperLimit = upperLimit;
formatter = FormatDefault;
if (validatorType == null || validatorType == typeof(bool)) {
validator = ValidateBool;
parser = ParseBool;
formatter = FormatBool;
}
else if (validatorType == typeof(float)) {
validator = ValidateFloat;
parser = ParseFloat;
}
else if (validatorType == typeof(int)) {
validator = ValidateInt;
parser = ParseInt;
}
else if (validatorType == typeof(ulong)) {
validator = ValidateUlong;
parser = ParseUlong;
}
else if (validatorType == typeof(string)) {
validator = ValidateString;
parser = ParseString;
formatter = FormatString;
}
valueType = validatorType;
}
public object GetSet {
get {
return getter();
}
set {
//validate first
if (validator(value)) {
ReusableObject = parser(value);
setter(ReusableObject);
//tell the players
TellMessage(null, MSG(MSG_VALGP_HAS_BEEN_SET, null, name, formatter(ReusableObject)), true);
Instance.SaveConfigData();
}
else {
ReusableString = upperLimit != double.PositiveInfinity || lowerLimit != double.NegativeInfinity ? $"The value for {name} is either too low or too high.Try {FormatAcceptable()}" : $"Incorrect value for {name}.You need to enter {FormatAcceptable()}";
TellMessage(null, $"{ReusableString} \nThe value remains as {formatter(getter())}.");
}
}
}
public string FormatAcceptable() {
return valueType == typeof(bool) ? "logical values (<color=green>true</color> or <color=red>false</color>)" : valueType == typeof(string) ? FormatStrings() : FormatNumericLimits();
}
private string FormatNumericLimits() {
ReusableString2 = valueType == typeof(float) ? "<color=green>fractions (like 1.2345)</color>" : "<color=purple>integers (like 12345)</color>";
return $"{ReusableString2} between <color=red>{lowerLimit.ToString("0.00")}</color> and <color=blgp>{upperLimit.ToString("0.00")}";
}
private string FormatStrings() {
return "hexadecimal numbers WITHOUT preceeding # (like 3db4b3)";
//return "strings (like ThisIsAString) - if they have a space, in quotes (like \"This Is A String\")";
}
private string FormatString(object value) {
return $"<color=yellow>{value?.ToString() ?? "NULL"}</color>";
}
private string FormatBool(object value) {
return value.Equals(true) ? "<color=green>true</color>" : "<color=red>false</color>";
}
private object ParseBool(object value) {
ReusableString2 = value.ToString().ToLower();
ReusableBool2 = !(ReusableString2.Contains("f") || ReusableString2.Contains("0") || ReusableString2.Contains("no"));
return ReusableBool2;
}
private bool ValidateBool(object value) {
//whatever it is, it can be always treated as bool
return true;
}
private object ParseFloat(object value) {
//it's already in ReusableFloat! neat, huh.//since we're using it, it must've been validated, so it's still there eh.return ReusableFloat2;
}
private object ParseString(object value) {
return ReusableString;
}
private object ParseInt(object value) {
return ReusableInt2;
}
private object ParseUlong(object value) {
return ReusableUlong2;
}
private bool ValidateFloat(object value) {
if (float.TryParse(value.ToString(), out ReusableFloat2)) {
return (ReusableFloat2 >= lowerLimit && ReusableFloat2 <= upperLimit);
}
else return false;
}
private bool ValidateInt(object value) {
if (int.TryParse(value.ToString(), out ReusableInt2)) {
return (ReusableInt2 >= lowerLimit && ReusableInt2 <= upperLimit);
}
else return false;
}
private bool ValidateUlong(object value) {
if (ulong.TryParse(value.ToString(), out ReusableUlong2)) {
return (ReusableUlong2 >= lowerLimit && ReusableUlong2 <= upperLimit);
}
else return false;
}
private bool ValidateString(object value) {
ReusableString = value.ToString();
if (ReusableString.ToLower() == "null") {
ReusableString = null;
}
return true;
}
private string FormatDefault(object value) {
return $"<color=#00FFFF>{value}</color>";
}
}
private static Dictionary<string, InteractiveConfigValue> ConfigValues = null;
private static void AddInteractiveConfigValue(string name, string description, Func<object> getter, Action<object> setter, Type validator = null, double lowerLimit = double.NegativeInfinity, double upperLimit = double.PositiveInfinity) {
ConfigValues.Add(name, new InteractiveConfigValue(name, description, getter, setter, validator, lowerLimit, upperLimit));
}
private static void GenerateAllInteractiveConfigValues() {
ConfigValues = new Dictionary<string, InteractiveConfigValue>();
AddInteractiveConfigValue("GridConstantPower", $"If set to true, the Grid will always produce electricity at its peak (100%), 24 hours a day.", () => Instance.Configuration.GridConstantPower, val => {
Instance.Configuration.GridConstantPower = (bool)val;
}
, typeof(bool));
AddInteractiveConfigValue("GridProductionStartAtHour", $"The hour of the day when the power production starts climbing up from 0", () => Instance.Configuration.GridProductionStartAtHour, val => {
Instance.Configuration.GridProductionStartAtHour = (float)val;
}
, typeof(float), 0, 24);
AddInteractiveConfigValue("GridProductionEndAtHour", $"The hour of the day when the power production settles back at 0", () => Instance.Configuration.GridProductionEndAtHour, val => {
Instance.Configuration.GridProductionEndAtHour = (float)val;
}
, typeof(float), 0, 24);
AddInteractiveConfigValue("StreetlightsEnable", $"If set to false, no Street Lights will spawn and existing ones will be removed.", () => Instance.Configuration.StreetlightsEnable, val => {
Instance.Configuration.StreetlightsEnable = (bool)val;
StreetlightsSet((bool)val);
}
, typeof(bool));
AddInteractiveConfigValue("StreetlightsConstantPower", $"If set to true, the Streetlights will be on 24 hours a day.", () => Instance.Configuration.StreetlightsConstantPower, val => {
Instance.Configuration.StreetlightsConstantPower = (bool)val;
}
, typeof(bool));
AddInteractiveConfigValue("StreetlightsTurnOnAtHour", $"The hour of the day when the street lights turn on", () => Instance.Configuration.StreetlightsTurnOnAtHour, val => {
Instance.Configuration.StreetlightsTurnOnAtHour = (float)val;
}
, typeof(float), 0, 24);
AddInteractiveConfigValue("StreetlightsTurnOffAtHour", $"The hour of the day when the street lights turn off", () => Instance.Configuration.StreetlightsTurnOffAtHour, val => {
Instance.Configuration.StreetlightsTurnOffAtHour = (float)val;
}
, typeof(float), 0, 24);
AddInteractiveConfigValue("StreetlightsReliability", $"The reliability of the streetlight.The less it is, the more often it will flicker.At 1, which represents 100%, it never flickers.", () => Instance.Configuration.StreetlightsReliability, val => {
Instance.Configuration.StreetlightsReliability = (float)val;
}
, typeof(float), 0F, 1F);
AddInteractiveConfigValue("StreetlightsFlickerLengthMin", $"The minimum random length of the flicker, in seconds", () => Instance.Configuration.StreetlightsFlickerLengthMin, val => {
Instance.Configuration.StreetlightsFlickerLengthMin = (float)val;
}
, typeof(float), 0F, 10F);
AddInteractiveConfigValue("StreetlightsFlickerLengthMax", $"The maximum random length of the flicker, in seconds", () => Instance.Configuration.StreetlightsFlickerLengthMax, val => {
Instance.Configuration.StreetlightsFlickerLengthMax = (float)val;
}
, typeof(float), 0F, 10F);
AddInteractiveConfigValue("GeneratorChancePowerlineFunctional", $"The chance that a valid Power Line Pole will be made functional during generation.0 represents 0%, 0.5 represents 50%, 1 represents 100%", () => Instance.Configuration.GeneratorChancePowerlineFunctional, val => {
Instance.Configuration.GeneratorChancePowerlineFunctional = (float)val;
}
, typeof(float), 0, 1);
AddInteractiveConfigValue("FuseRequired", $"If set to false, the Transformers won't need Fuses to produce power, just the right time of the day (if power is not 24/7)", () => Instance.Configuration.FuseRequired, val => {
Instance.Configuration.FuseRequired = (bool)val;
}
, typeof(bool));
AddInteractiveConfigValue("BuildingBlockPreventsButtonPress", $"If set to to true, if there's any Tool Cupboards in the range of the Transformer, you need to be authorised on the closest (or oldest, check config below) of them to open the Transformer GUI", () => Instance.Configuration.BuildingBlockPreventsButtonPress, val => {
Instance.Configuration.BuildingBlockPreventsButtonPress = (bool)val;
}
, typeof(bool));
AddInteractiveConfigValue("BuildingBlockPreventsRootCombinerDamage", $"If set to to true, if there's any Tool Cupboards in the range of the Transformer, you need to be authorised on the closest (or oldest, check config below) of them to damage the pole Root Combiners", () => Instance.Configuration.BuildingBlockPreventsRootCombinerDamage, val => {
Instance.Configuration.BuildingBlockPreventsRootCombinerDamage = (bool)val;
}
, typeof(bool));
AddInteractiveConfigValue("BuildingBlockPreventsRootCombinerPickup", $"If set to to true, if there's any Tool Cupboards in the range of the Transformer, you need to be authorised on the closest (or oldest, check config below) of them to pick up the pole Root Combiners", () => Instance.Configuration.BuildingBlockPreventsRootCombinerPickup, val => {
Instance.Configuration.BuildingBlockPreventsRootCombinerPickup = (bool)val;
}
, typeof(bool));
AddInteractiveConfigValue("BuildingBlockPreventsWireManipulation", $"If set to to true, if there's any Tool Cupboards in the range of the Transformer, you need to be authorised on the closest (or oldest, check config below) of them to connect/disconnect wires to/from the pole Root Combiners", () => Instance.Configuration.BuildingBlockPreventsWireManipulation, val => {
Instance.Configuration.BuildingBlockPreventsWireManipulation = (bool)val;
}
, typeof(bool));
AddInteractiveConfigValue("BuildingBlockPrioritiseCloserIfTrueOlderIfFalse", $"If set to to true, if there's any Tool Cupboards in the range of the Transformer, the closest TC to the pole will be considered the most dominant one, otherwise, it's the that was placed there first", () => Instance.Configuration.BuildingBlockPrioritiseCloserIfTrueOlderIfFalse, val => {
Instance.Configuration.BuildingBlockPrioritiseCloserIfTrueOlderIfFalse = (bool)val;
}
, typeof(bool));
AddInteractiveConfigValue("GeneratorInitialLevelMin", $"The lower limit for the random upgrade level of valid Power Line Poles during generation", () => Instance.Configuration.GeneratorInitialLevelMin, val => {
Instance.Configuration.GeneratorInitialLevelMin = (int)val;
}
, typeof(int), 0, 1000);
AddInteractiveConfigValue("GeneratorInitialLevelMax", $"The upper limit for the random upgrade level of valid Power Line Poles during generation", () => Instance.Configuration.GeneratorInitialLevelMax, val => {
Instance.Configuration.GeneratorInitialLevelMax = (int)val;
}
, typeof(int), 0, 1000);
AddInteractiveConfigValue("GeneratorInitialOutletsMin", $"The lower limit for the random number of Outlets of valid Power Line Poles during generation", () => Instance.Configuration.GeneratorInitialOutletsMin, val => {
Instance.Configuration.GeneratorInitialOutletsMin = (int)val;
}
, typeof(int), 0, 4);
AddInteractiveConfigValue("GeneratorInitialOutletsMax", $"The upper limit for the random number of Outlets of valid Power Line Poles during generation", () => Instance.Configuration.GeneratorInitialOutletsMax, val => {
Instance.Configuration.GeneratorInitialOutletsMax = (int)val;
}
, typeof(int), 0, 4);
AddInteractiveConfigValue("PowerlinePowerPerTechTrash", $"How much RWs at peak hours are provided per 1 level upgrade", () => Instance.Configuration.PowerlinePowerPerTechTrash, val => {
Instance.Configuration.PowerlinePowerPerTechTrash = (int)val;
}
, typeof(int), 0, 1000);
AddInteractiveConfigValue("PowerlineMaxTechTrashLevel", $"The maximum level that a Transformer can be upgraded to", () => Instance.Configuration.PowerlineMaxTechTrashLevel, val => {
Instance.Configuration.PowerlineMaxTechTrashLevel = (int)val;
}
, typeof(int), 0, 1000);
AddInteractiveConfigValue("PowerlineFuseDurationSeconds", $"How long (in seconds) a brand new Fuse inserted in the Transformer Fusebox will last", () => Instance.Configuration.PowerlineFuseDurationSeconds, val => {
Instance.Configuration.PowerlineFuseDurationSeconds = (float)val;
}
, typeof(float), 0, float.PositiveInfinity);
AddInteractiveConfigValue("FuseItemShortname", $"The shortname of the item that acts as the Fuse for Transformer Fuseboxes, must be a valid Rust item shortname", () => Instance.Configuration.FuseItemShortname, val => {
Instance.Configuration.FuseItemShortname = (string)val;
}
, typeof(string));
AddInteractiveConfigValue("FuseItemGuiName", $"The custom GUI display name of the item that acts as the Fuse for Transformer Fuseboxes, can be anything, or left blank for default", () => Instance.Configuration.FuseItemGuiName, val => {
Instance.Configuration.FuseItemGuiName = (string)val;
}
, typeof(string));
AddInteractiveConfigValue("FuseItemSkinID", $"The skin ID of the Fuse item if using custom, leave at 0 to accept any items of thae shortname specified by FuseItemShortname", () => Instance.Configuration.FuseItemSkinID, val => {
Instance.Configuration.FuseItemSkinID = (ulong)val;
}
, typeof(ulong));
AddInteractiveConfigValue("AutoLaddersEnabled", $"If set to true, every functional powerline pole will be automatically equipped with an indestructible set of ladders, instead of letting players deploy them", () => Instance.Configuration.AutoLaddersEnabled, val => {
Instance.Configuration.AutoLaddersEnabled = (bool)val;
}
, typeof(bool));
}
#endregion
#region CONFIG PERMISSION PROFILES
public class PermissionProfile {
public string PermissionRequired = "";
public bool GridCanDeployLadder = true;
public bool GridCanDeployRootCombiners = true;
public bool GridCanConnectDisconnect = true;
public bool GridCanPressButton = true;
public bool GridCanUpgrade = true;
public float GridDangerousWireElectricutionChance = 0.1F;
}
private PermissionProfile AdminPermissionProfile() {
return new PermissionProfile {
PermissionRequired = PERM_ADMIN, GridDangerousWireElectricutionChance = 0F,
}
;
}
private PermissionProfile DefaultPermissionProfile() {
return new PermissionProfile {
PermissionRequired = "default",
}
;
}
private PermissionProfile VIPPermissionProfile() {
return new PermissionProfile {
PermissionRequired = PERM_VIP1,
}
;
}
private static PermissionProfile GetPermissionProfile(BasePlayer player) {
foreach (var prof in Instance.Configuration.PermissionProfiles) {
if (prof.Key == "default") continue;
if (prof.Key == PERM_ADMIN) {
if (HasAdminPermission(player)) {
return prof.Value;
}
;
}
if (Instance.permission.UserHasPermission(player.UserIDString, prof.Key)) {
return prof.Value;
}
}
//we got here so that means nothing found.if (!Instance.Configuration.PermissionProfiles.ContainsKey("default"))
{
//oops, data must be fucked.Instance.Configuration.PermissionProfiles.Add("default", Instance.DefaultPermissionProfile());
Instance.SaveConfigData();
}
return Instance.Configuration.PermissionProfiles["default"];
}
#endregion
#region REUSABLES
private static object ReusableObject;
private static bool ReusableBool;
private static string ReusableString;
private static float ReusableFloat;
private static bool ReusableBool2;
private static string ReusableString2;
private static float ReusableFloat2;
private static int ReusableInt2;
private static ulong ReusableUlong2;
#endregion
#region DATA
public StoredData Data;
public void ProcessData(string previousConfigVersion) {
bool needsSave = false;
bool needsWipe = Data.CommunityEntityNetID != CommunityEntity.ServerInstance.net.ID.Value;
if (needsWipe || Data.PowerlineData == null || Data.PowerlineData?.Count == 0) {
Instance.PrintError($"Fresh wipe or empty data detected, clearing old data, locating powerline poles and creating data entries for them...");
Data.CommunityEntityNetID = CommunityEntity.ServerInstance.net.ID.Value;
Data.PowerlineData = new Hash<ulong, SerializablePowerlinePole>();
Data.LastPowerlineDataID = 0;
var removed = TryEmergencyCleanup();
if (removed > 0) {
Instance.PrintWarning($"Found and removed {removed} leftover entities");
}
//iterate over everything to get positions
//take chance of occurence into account
string prefabName;
GameObject prefabGameObject;
uint validPolesFound = 0;
uint totalPolesTurned = 0;
uint blockedPolesFound = 0;
foreach (ProtoBuf.PrefabData entry in World.Serialization.world.prefabs) {
prefabName = StringPool.Get(entry.id);
if (prefabName != PREFAB_POWERLINE_POLE_LIGHTS_1 && prefabName != PREFAB_POWERLINE_POLE_LIGHTS_2) {
continue;
}
prefabGameObject = GameManager.server.FindPrefab(prefabName);
if (prefabGameObject == null) {
continue;
}
if (!TestPlacingCloseToMonument(entry.position)) {
blockedPolesFound++;
continue;
}
validPolesFound++;
//maybe skip it - what does the config chance say about it?
if (UnityEngine.Random.Range(0F, 1F) > Configuration.GeneratorChancePowerlineFunctional) {
continue;
}
PoleDataAdd(entry.position, entry.rotation, validPolesFound);
totalPolesTurned++;
}
if (blockedPolesFound > 0) {
Instance.PrintError($"INFO: Found {blockedPolesFound} poles blocked by a monument or a building block volume nearby - those have been skipped.");
}
if (validPolesFound == 0) {
Instance.PrintError("ERROR: Your map doesn't seem to contain any valid, non - building blocked Power Line Poles.Please review your map.");
Data.PowerlineData = null;
}
else {
if (totalPolesTurned == 0) {
Instance.PrintWarning($"WARNING: Although {validPolesFound} valid Powerline Poles have been found on your map, none of them were lucky enough to be turned into functional ones.Please review your Generation chance settings in the config and maybe crank it up.OR, reload the plugin and hope for better random results next time.");
Data.PowerlineData = null;
}
else {
Instance.PrintWarning($"Found {validPolesFound} valid (non - building blocked) Powerline Poles and saved {totalPolesTurned} of them to data as functional.");
}
}
needsSave = true;
}
//powerline pole helpers...int spawnedHelpers = 0;
if (Data.PowerlineData != null) {
foreach (var dataEntry in Data.PowerlineData) {
PowerlinePoleHelper.AddHelperPowerlinePole(dataEntry.Value);
spawnedHelpers++;
}
}
if (spawnedHelpers > 0) {
Instance.PrintWarning($"Successfully spawned {spawnedHelpers} functional powerlines and their entities");
if (previousConfigVersion != Configuration.Version) {
Instance.PrintWarning($"Looks like you're migrating from {previousConfigVersion} to {Configuration.Version}, checking if your Data needs post-processing...");
//currently nothing neeeds to be done here/
}
}
else {
Instance.PrintWarning("It doesn't look like you have any functional Power Line Poles.");
}
if (needsSave) {
SaveData();
}
}
public void LoadData() {
PrintWarning("Loading data...");
try {
Data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
}
catch {
PrintWarning("Corrupt data, generating default one.");
NewData();
}
if (Data == null) {
PrintWarning("Null data, generating default one.");
NewData();
}
}
public void SaveData() {
PrintWarning("Saving data...");
Interface.Oxide.DataFileSystem.WriteObject(Name, Data);
Data.Dirty = false;
}
public void NewData() {
Data = new StoredData();
SaveData();
}
public class StoredData {
public Hash<ulong, SerializablePowerlinePole> PowerlineData = new Hash<ulong, SerializablePowerlinePole>();
public ulong LastPowerlineDataID = 0;
public ulong CommunityEntityNetID = CommunityEntity.ServerInstance.net.ID.Value;
[JsonIgnore]
public bool Dirty = false;
}
public class SerializableVector3 {
public float PosX;
public float PosY;
public float PosZ;
[JsonConstructor]
public SerializableVector3() {
PosX = 0F;
PosY = 0F;
PosY = 0F;
}
public SerializableVector3(Vector3 vector3) {
PosX = vector3.x;
PosY = vector3.y;
PosZ = vector3.z;
}
public SerializableVector3(float x, float y, float z) {
PosX = x;
PosY = y;
PosZ = z;
}
public static implicit operator Vector3(SerializableVector3 vector3) {
return new Vector3(vector3.PosX, vector3.PosY, vector3.PosZ);
}
}
public class SerializableTransform {
public SerializableVector3 Position;
public SerializableVector3 Rotation;
}
public class SerializablePole {
public ulong Uid;
public SerializableTransform Transform;
}
public class SerializablePowerlinePole : SerializablePole {
//we will identify powerline poles by their order
public bool[] OutletStates;
public int TechTrashInside = 0;
[JsonIgnore]
public bool Dirty {
set {
Instance.Data.Dirty = true;
}
}
}
#endregion
#region LANG
public const string MSG_CHAT_PREFIX = "MSG_CHAT_PREFIX";
public const string MSG_TECH_TRASH = "MSG_TECH_TRASH";
public const string MSG_VALGP_HAS_BEEN_SET = "MSG_VALGP_HAS_BEEN_SET";
public const string MSG_CFG_RUNDOWN_FORMAT = "MSG_CFG_RUNDOWN_FORMAT";
public const string MSG_CFG_DETAILS_FORMAT = "MSG_CFG_DETAILS_FORMAT";
public const string MSG_CFG_NO_SETTING_FOUND = "MSG_CFG_NO_SETTING_FOUND";
public const string MSG_CFG_DEFAULT = "MSG_CFG_DEFAULT";
public const string MSG_POWERLINE_GUI_TITLE = "MSG_POWERLINE_GUI_TITLE";
public const string MSG_POWERLINE_STATUS_NO_OUTLETS = "MSG_POWERLINE_STATUS_NO_OUTLETS";
public const string MSG_POWERLINE_STATUS_NO_TECHTRASH = "MSG_POWERLINE_STATUS_NO_TECHTRASH";
public const string MSG_POWERLINE_STATUS_NO_FUSE = "MSG_POWERLINE_STATUS_NO_FUSE";
public const string MSG_POWERLINE_POWER_CONSTANT = "MSG_POWERLINE_POWER_CONSTANT";
public const string MSG_POWERLINE_POWER_BETWEEN = "MSG_POWERLINE_POWER_BETWEEN";
public const string MSG_POWERLINE_POWER_RECEIVING = "MSG_POWERLINE_POWER_RECEIVING";
public const string MSG_POWERLINE_FUSE_WILL_LAST_FOR = "MSG_POWERLINE_FUSE_WILL_LAST_FOR";
public const string MSG_POWERLINE_TIME_FORMAT_HOUR_MINUTE = "MSG_POWERLINE_TIME_FORMAT_OCLOCK";
public const string MSG_POWERLINE_TIME_FORMAT_HOUR_MINUTE_SECOND = "MSG_POWERLINE_TIME_FORMAT_FULL";
public const string MSG_POWERLINE_BODY_OUTLET_CURRENTLY_ROUTING = "MSG_POWERLINE_BODY_OUTLET_CURRENTLY_ROUTING";
public const string MSG_POWERLINE_BODY_OUTLET_INSERT = "MSG_POWERLINE_BODY_OUTLET_INSERT";
public const string MSG_POWERLINE_BODY_OUTLET_ALL_THERE = "MSG_POWERLINE_BODY_OUTLET_ALL_THERE";
public const string MSG_POWERLINE_BODY_TECHTRASH = "MSG_POWERLINE_BODY_TECHTRASH";
public const string MSG_POWERLINE_BODY_FUSE_TIME_LEFT = "MSG_POWERLINE_BODY_FUSE";
public const string MSG_POWERLINE_BODY_FUSE_WILL_LAST = "MSG_POWERLINE_BODY_FUSE_WILL_LAST";
public const string MSG_CANT_LOOT_FUSEBOX = "MSG_CANT_LOOT_FUSEBOX";
public const string MSG_CANT_PRESS_TRANSFORMER_BUTTON = "MSG_CANT_PRESS_TRANSFORMER_BUTTON";
public const string MSG_SLOT_NAME_INTERNAL = "MSG_SLOT_NAME_INTERNAL";
public const string MSG_SLOT_NAME_GRID = "MSG_SLOT_NAME_GRID";
public const string MSG_CANT_CONNECT_DISCONNECT_GRID = "MSG_CANT_CONNECT_DISCONNECT_GRID";
public const string MSG_CANT_DEPLOY_LADDER = "MSG_CANT_DEPLOY_LADDER";
public const string MSG_CANT_UPGRADE = "MSG_CANT_UPGRADE";
public const string MSG_POWERLINE_BODY_FUSE_NOT_REQUIRED = "MSG_POWERLINE_BODY_FUSE_NOT_REQUIRED";
public const string MSG_CANT_PRESS_BUILDING_BLOCK = "MSG_CANT_PRESS_BUILDING_BLOCK";
public const string MSG_WIRE_SLACK_ENABLED = "MSG_WIRE_SLACK_ENABLED";
public const string MSG_WIRE_SLACK_DISABLED = "MSG_WIRE_SLACK_DISABLED";
public const string MSG_FUSE = "MSG_FUSE";
public const string MSG_POWERLINE_STATUS_NO_FUSE_NEW = "MSG_POWERLINE_STATUS_NO_FUSE_NEW";
public const string MSG_POWERLINE_BODY_FUSE_TIME_LEFT_NEW = "MSG_POWERLINE_BODY_FUSE_TIME_LEFT_NEW";
public const string MSG_POWERLINE_BODY_FUSE_WILL_LAST_NEW = "MSG_POWERLINE_BODY_FUSE_WILL_LAST_NEW";
public const string MSG_POWERLINE_BODY_FUSE_NOT_REQUIRED_NEW = "MSG_POWERLINE_BODY_FUSE_NOT_REQUIRED_NEW";
private static Dictionary<string, string> LangMessages = new Dictionary<string, string> {
[MSG_CHAT_PREFIX]
= $"<color=#57ff77>[Grid Power]</color>", [MSG_TECH_TRASH] = "Tech Trash", [MSG_VALGP_HAS_BEEN_SET] = "Config value <color=yellow>{0}</color> has been set to {1} by an admin.", [MSG_CFG_DEFAULT] = "Here's th
// ... [Code preview truncated due to length. Full source in attached file] ...