C#:
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 System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Oxide.Plugins {
[Info("GridPower", "Nikedemos", VERSION)]
[Description("Allows players to take over, maintain and upgrade powerline poles to draw electric power")]
public class GridPower : RustPlugin {
#region CONST & STATIC
private const string VERSION = "1.0.4";
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 ITEM_LIGHTS = "xmas.lightstring.advanced";
public const string ITEM_WIRETOOL = "wiretool";
public const string ITEM_HOSETOOL = "hosetool";
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 string PREFAB_POWERLINE_POLE_LIGHTS_YES = "assets/bundled/prefabs/autospawn/decor/powerline-small/powerline_pole_a.prefab";
public const string PREFAB_POWERLINE_POLE_LIGHTS_NO = "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_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;
public static Color[] XmasColorSequence;
private static bool Unloading = false;
private static bool NewSave = false;
private static bool EmergencyCleanup = false;
public static ListHashSet<string> ValidTools;
public static ItemDefinition[] FuseboxAllowedItems;
#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
private ConfigData Configuration;
public class ConfigData {
public string Version = VERSION;
public Dictionary<string, PermissionProfile> PermissionProfiles = new Dictionary<string, PermissionProfile>();
public Dictionary<string, List<ulong>> ProtectiveClothing = new Dictionary<string, List<ulong>>();
public float GeneratorChancePowerlineFunctional = 0.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;
public string UpgradeItemShortname = ITEM_TECHTRASH;
public ulong UpgradeItemSkinID = 0;
public string UpgradeItemGuiIconURL = string.Empty;
public string UpgradeItemGuiName = string.Empty;
}
protected override void LoadDefaultConfig() {
RestoreDefaultConfig();
}
private void ProcessConfigData() {
bool needsSave = false;
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;
}
if (Configuration.Version != VERSION) {
var oldVersion = Configuration.Version;
if (oldVersion == "1.0.0") {
Instance.PrintWarning($"Looks like you're updating from 1.0.0 to {VERSION}.The new version replaces dummy generator prefabs from static to deployed.The plugin will now locate all static generators on the map that have been marked for internal use by PowerGrid and replace them with the deployed ones...");
var count = 0;
foreach (var generator in BaseNetworkable.serverEntities.OfType<ElectricGenerator>()) {
if (generator.OwnerID != 0) {
continue;
}
if (generator.PrefabName != PREFAB_GENERATOR_OLD) {
continue;
}
if (!IsMarkedInternal(generator)) {
continue;
}
var pos = generator.transform.position;
var rot = generator.transform.eulerAngles;
generator.Kill(BaseNetworkable.DestroyMode.None);
var newGenerator = SpawnSpecial<ElectricGenerator>(PREFAB_GENERATOR_NEW, pos, rot, true);
count++;
}
if (count == 0) {
Instance.PrintWarning("No static generators found that met the criteria, nothing replaced.");
}
else {
Instance.PrintWarning($"Replaced {count} static generators with deployed ones.");
}
}
Configuration.Version = VERSION;
needsSave = true;
}
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 FormatEnumCargo(object value) {
return $"<color=red>{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 bool ValidateEnumCargo(object value) {
ReusableString = value.ToString().ToLower();
if (ReusableString == "" || ReusableString == string.Empty) {
return false;
}
if (!(ReusableString.Contains("warn") || ReusableString.Contains("prevent") || ReusableString.Contains("none"))) {
return false;
}
return true;
}
private string FormatDefault(object value) {
return $"<color=#00FFFF>{value.ToString()}</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 all of them to open the Transformer GUI", () => Instance.Configuration.BuildingBlockPreventsButtonPress, val => {
Instance.Configuration.BuildingBlockPreventsButtonPress = (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);
}
#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;
public bool HangingXmasLights = true;
public bool HangingWiresAndHoses = true;
public int SubdivisionsPreview = 10;
public int SubdivisionsFinal = 50;
public float SlackMax = 5F;
}
private PermissionProfile AdminPermissionProfile() {
return new PermissionProfile {
PermissionRequired = PERM_ADMIN, HangingWiresAndHoses = true, HangingXmasLights = true, SlackMax = 20F, GridDangerousWireElectricutionChance = 0F,
}
;
}
private PermissionProfile DefaultPermissionProfile() {
return new PermissionProfile {
PermissionRequired = "default", HangingWiresAndHoses = false, HangingXmasLights = false, SlackMax = 5F,
}
;
}
private PermissionProfile VIPPermissionProfile() {
return new PermissionProfile {
PermissionRequired = PERM_VIP1, HangingWiresAndHoses = true, HangingXmasLights = true, SlackMax = 10F,
}
;
}
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() {
bool needsSave = false;
if (NewSave || 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.PowerlineData = new Hash<uint, SerializablePowerlinePole>();
Data.PlayerWireConfigs = new Hash<ulong, MultiToolConfig>();
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;
SerializablePowerlinePole currentDataPowerPole;
foreach (ProtoBuf.PrefabData entry in World.Serialization.world.prefabs) {
prefabName = StringPool.Get(entry.id);
if (prefabName != PREFAB_POWERLINE_POLE_LIGHTS_YES && prefabName != PREFAB_POWERLINE_POLE_LIGHTS_NO) {
continue;
}
prefabGameObject = GameManager.server.FindPrefab(prefabName);
if (prefabGameObject == null) {
continue;
}
if (IsPlacementBlockedAt(entry.position)) {
continue;
}
validPolesFound++;
//maybe skip it - what does the config chance say about it?
if (UnityEngine.Random.Range(0F, 1F) > Configuration.GeneratorChancePowerlineFunctional) {
continue;
}
int outlets = Mathf.Clamp(UnityEngine.Random.Range(Configuration.GeneratorInitialOutletsMin, Configuration.GeneratorInitialOutletsMax), 0, 4);
currentDataPowerPole = new SerializablePowerlinePole {
Uid = validPolesFound, TechTrashInside = 1, //use config value
OutletStates = new bool[4] {
outlets > 0, outlets > 1, outlets > 2, outlets > 3,
}
, Transform = new SerializableTransform {
Position = new SerializableVector3(entry.position), Rotation = new SerializableVector3(entry.rotation)
}
}
;
Data.PowerlineData.Add(validPolesFound, currentDataPowerPole);
totalPolesTurned++;
}
if (validPolesFound == 0) {
Instance.PrintError("ERROR: Your map doesn't seem to contain any valid, non - placement 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-placement blocked) Powerline Poles and saved {totalPolesTurned} of them to data as functional.");
}
}
needsSave = true;
}
//powerline pole helpers...int spawnedHelpers = 0;
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 (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<uint, SerializablePowerlinePole> PowerlineData = new Hash<uint, SerializablePowerlinePole>();
public Hash<ulong, MultiToolConfig> PlayerWireConfigs = new Hash<ulong, MultiToolConfig>();
[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 uint 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;
}
}
}
public class MultiToolConfig {
public float Slack = 0.5F;
[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_PLACEHOLDER_29 = "MSG_PLACEHOLDER_29";
public const string MSG_PLACEHOLDER_30 = "MSG_PLACEHOLDER_30";
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 the current settings.Type <color=yellow>/gp_cfg settingName</color> with no parameters to see the description, the current value and what the accepted arguments are.Type <color=yellow>/gp_cfg settingName acceptedValue</color> to change the setting.", [MSG_CFG_RUNDOWN_FORMAT] = "/gp_cfg <color=yellow>{0}</color> (currently: {1})", [MSG_CFG_DETAILS_FORMAT] = "<color=green>{0}</color>:\n{1} ({2})\nThis value is currently set to: {3}\n", [MSG_CFG_NO_SETTING_FOUND] = "No setting with that name has been found.Type /gp_cfg to get a rundown.", [MSG_POWERLINE_GUI_TITLE] = "GRID TRANSFORMER", [MSG_POWERLINE_POWER_BETWEEN] = "Grid emits power from {0} to {1} (peak at {2})", [MSG_POWERLINE_POWER_RECEIVING] = "Receiving power from Grid at {0}% efficiency", [MSG_POWERLINE_POWER_CONSTANT] = "Grid emits power 24/7 at a 100% efficiency", [MSG_POWERLINE_FUSE_WILL_LAST_FOR] = "Each fuse inserted in the Fuse Box will last for approx.{0}", [MSG_POWERLINE_TIME_FORMAT_HOUR_MINUTE] = "{0}:{1}", [MSG_POWERLINE_TIME_FORMAT_HOUR_MINUTE_SECOND] = "{0}:{1}:{2}", [MSG_POWERLINE_STATUS_NO_TECHTRASH] = "No {0} applied, insert some in the Fuse Box below", [MSG_POWERLINE_STATUS_NO_OUTLETS] = "No Root Combiners, insert some in the Fuse Box below", [MSG_POWERLINE_STATUS_NO_FUSE] = "No Fuse inside, insert one in the Fuse Box below", [MSG_POWERLINE_BODY_OUTLET_CURRENTLY_ROUTING] = "Currently routing {0} RW from Grid over {1} Outlets", [MSG_POWERLINE_BODY_OUTLET_INSERT] = "{0} out of 4 outlets present, insert Root Combiners to add more", [MSG_POWERLINE_BODY_OUTLET_ALL_THERE] = "4 out of 4 outlets present", [MSG_POWERLINE_BODY_TECHTRASH] = "Upgrade level: {0} ({1} max level)\nPeak output: {2} RW ({3} RW at max level)\nEach {5} inserted permanently increases peak output by {4} RW", [MSG_POWERLINE_BODY_FUSE_TIME_LEFT] = "Fuse condition: {0}%\nEstimated time left: {1}", [MSG_POWERLINE_BODY_FUSE_WILL_LAST] = "Each fresh fuse inside the transformer will last for approximately {0}", [MSG_CANT_LOOT_FUSEBOX] = "Can't loot the Fusebox.Is another plugin preventing it?", [MSG_SLOT_NAME_INTERNAL] = "INTERNAL SLOT", [MSG_SLOT_NAME_GRID] = "Grid Out {0}", [MSG_CANT_PRESS_TRANSFORMER_BUTTON] = "You don't have permission to open the Grid Transformer.", [MSG_CANT_CONNECT_DISCONNECT_GRID] = "You don't have permission to connect/disconnect to/from the Grid Transformer.", [MSG_CANT_DEPLOY_LADDER] = "You don't have permission to deploy ladders on Powerline poles.", [MSG_CANT_UPGRADE] = "You don't have permission to upgrade Grid Transformers with {0}.", [MSG_POWERLINE_BODY_FUSE_NOT_REQUIRED] = "Fuses are not required on this server.", [MSG_CANT_PRESS_BUILDING_BLOCK] = "Cannot access the Transformer, you're building blocked", [MSG_PLACEHOLDER_29] = "Placeholder", [MSG_PLACEHOLDER_30] = "Placeholder",
}
;
private static string MSG(string msg, string userID = null, params object[] args) {
if (args == null) {
return Instance.lang.GetMessage(msg, Instance, userID);
}
else {
return string.Format(Instance.lang.GetMessage(msg, Instance, userID), args);
}
}
#endregion
#region MONO
//attach to empty game object, destroy at unload
public class PowerGrid : CommonBehaviour {
public static PowerGrid TheOnlyInstance;
public float GlobalEfficiency = 0F;
public bool GridProducing = false;
public bool StreetlightsOn = false;
public void Prepare() {
UpdateRate = 1F;
UpdateRateRandomization = 0.015F;
UpdatePowerProduction();
TheOnlyInstance = this;
}
public void UpdatePowerProduction() {
if (Instance.Configuration.GridConstantPower) {
GridProducing = true;
GlobalEfficiency = 1F;
return;
}
GlobalEfficiency = GetGlobalPowerProduction(GetCurrentHour());
GridProducing = GlobalEfficiency > 0F;
StreetlightsOn = IsItTime(GetCurrentHour(), Instance.Configuration.StreetlightsTurnOnAtHour, Instance.Configuration.StreetlightsTurnOffAtHour);
}
public override void PerformUpdate() {
UpdatePowerProduction();
}
void OnDestroy() {
TheOnlyInstance = null;
}
}
public class PowerlinePoleOutlet {
public PowerlinePoleHelper PoleHelper;
public int Index;
public ElectricGenerator Generator;
public ElectricalCombiner RootCombiner;
public uint RootCombinerNetID;
public uint GeneratorNetID;
public bool Destroying = false;
public void Prepare(PowerlinePoleHelper helper, int index, Item insertedItem = null) {
Index = index;
PoleHelper = helper;
//summon battery and root combiner
if (insertedItem != null) {
Generator = SpawnSpecialRelativeTo<ElectricGenerator>(PoleHelper.transform, PREFAB_GENERATOR_NEW, PowerlinePoleHelper.PowerlineSocketPositions[Index] + Vector3.down * 15F, Vector3.zero, true);
RootCombiner = SpawnSpecialRelativeTo<ElectricalCombiner>(PoleHelper.transform, PREFAB_ROOT_COMBINER, PowerlinePoleHelper.PowerlineSocketPositions[Index], PowerlinePoleHelper.PowerlineSocketRotations[Index], false);
RootCombiner.SetHealth(insertedItem.conditionNormalized * RootCombiner.MaxHealth());
RootCombiner.skinID = insertedItem.skin;
insertedItem.UseItem();
PlayCombinerEffect(RootCombiner.transform.position);
}
else {
Generator = SummonSpecialRelativeTo<ElectricGenerator>(PoleHelper.transform, ref PowerlinePoleHelper.TemporaryVisibilityList, PREFAB_GENERATOR_NEW, PowerlinePoleHelper.PowerlineSocketPositions[Index] + Vector3.down * 15F, Vector3.zero, true);
RootCombiner = SummonSpecialRelativeTo<ElectricalCombiner>(PoleHelper.transform, ref PowerlinePoleHelper.TemporaryVisibilityList, PREFAB_ROOT_COMBINER, PowerlinePoleHelper.PowerlineSocketPositions[Index], PowerlinePoleHelper.PowerlineSocketRotations[Index], false);
}
GeneratorNetID = Generator.net.ID;
RootCombinerNetID = RootCombiner.net.ID;
Generator.pickup.enabled = false;
//RootCombiner.pickup.enabled = false;
MarkIOEntitySlotSpecial(RootCombiner.outputs[0], RootCombiner, false, true, MSG(MSG_SLOT_NAME_GRID, null, Index + 1));
// $"Grid Out {Index + 1}");
MarkIOEntitySlotSpecial(RootCombiner.inputs[0], RootCombiner, true, true);
MarkIOEntitySlotSpecial(RootCombiner.inputs[1], RootCombiner, true, true);
PoleHelper.UpdateAllOutputs();
EngageIO(RootCombiner, Generator, 0, 0, null, WireTool.WireColour.Default, true, false, true, true);
//network out the battery cause sure why not
/*
Battery.limitNetworking = true;
Battery.OnNetworkSubscribersLeave(Network.Net.sv.connections);
*/
PowerlinePoleHelper.AllIOEntitiesToPowerlinePole.Add(RootCombinerNetID, PoleHelper);
PowerlinePoleHelper.AllIOEntitiesToPowerlinePole.Add(GeneratorNetID, PoleHelper);
}
public void UpdateOutputPower(int newPower) {
if (
// ... [Code preview truncated due to length. Full source in attached file] ...