C#:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using UnityEngine;
using Random = UnityEngine.Random;
namespace Oxide.Plugins
{
[Info("Panel System", "Mevent", "1.1.0")]
internal class PanelSystem : RustPlugin
{
#region Fields
[PluginReference] private Plugin
ImageLibrary = null,
Notify = null,
Convoy = null,
ArmoredTrain = null,
WipeBlock = null,
UINotify = null;
private static PanelSystem _instance;
private const string Layer = "UI.PanelSystem";
private const string EventsLayer = "UI.PanelSystem.Events";
private const string SettingsLayer = "UI.PanelSystem.Settings";
private Dictionary<string, bool> _eventsCache = new Dictionary<string, bool>
{
["CH47"] = false,
["Bradley"] = false,
["Helicopter"] = false,
["Airdrop"] = false,
["CargoShip"] = false,
["Convoy"] = false,
["ArmoredTrain"] = false,
["WipeBlock"] = false
};
private readonly List<PanelSettings> _totalSettings = new List<PanelSettings>();
private enum PanelType
{
Online = 0,
Time = 1,
CH47 = 2,
Helicopter = 3,
Bradley = 4,
Airdrop = 5,
Economics = 6,
Custom = 7,
CargoShip = 8,
Button = 9,
Convoy = 10,
ArmoredTrain = 11,
WipeBlock = 12
}
private Dictionary<int, ButtonInfo> _buttonByID = new Dictionary<int, ButtonInfo>();
private const string _chatSay = "chat.say";
#endregion
#region Config
private Configuration _config;
private class Configuration
{
[JsonProperty(PropertyName = "Work with Notify?")]
public bool UseNotify = true;
[JsonProperty(PropertyName = "Display type (Overlay/Hud)")]
public string DisplayType = "Overlay";
[JsonProperty(PropertyName = "Logotype")]
public LogoSettings Logo = new LogoSettings
{
Enabled = true,
Permission = string.Empty,
Image = "https://i.imgur.com/eeuY1WI.png",
Command = string.Empty,
AnchorMin = "0 0", AnchorMax = "1 1",
OffsetMin = "-100 -110", OffsetMax = "-10 -20"
};
[JsonProperty(PropertyName = "Players Settings")]
public InfoPanel Players = new InfoPanel
{
Enabled = true,
Permission = string.Empty,
Image = "https://i.imgur.com/Ik9YSZS.png",
SettingsImage = "https://i.imgur.com/Ltn0Jr1.png",
Settings = new PanelSettings(true, string.Empty, true, false, true),
Size = new InfoPanel.InfoPanelSizeSettings(0, new List<SizeData>(), new List<InfoPanel.InfoPanelSize>
{
new InfoPanel.InfoPanelSize("sizeBig", string.Empty, 35, 12, 16, 40, 145),
new InfoPanel.InfoPanelSize("sizeMedium", string.Empty, 30, 10, 14, 40, 145),
new InfoPanel.InfoPanelSize("sizeSmall", string.Empty, 25, 8, 12, 40, 145)
})
};
[JsonProperty(PropertyName = "Time Settings")]
public TimeSettings Time = new TimeSettings
{
Enabled = true,
GameUpdate = false,
Permission = string.Empty,
Image = "https://i.imgur.com/ZI5vJk9.png",
SettingsImage = "https://i.imgur.com/MhfhTLP.png",
Settings = new PanelSettings(true, string.Empty, true, false, true),
Size = new InfoPanel.InfoPanelSizeSettings(0, new List<SizeData>(), new List<InfoPanel.InfoPanelSize>
{
new InfoPanel.InfoPanelSize("sizeBig", string.Empty, 35, 12, 16, 50, 145),
new InfoPanel.InfoPanelSize("sizeMedium", string.Empty, 30, 10, 14, 50, 145),
new InfoPanel.InfoPanelSize("sizeSmall", string.Empty, 25, 8, 12, 50, 145)
})
};
[JsonProperty(PropertyName = "Settings Button")]
public SettingsInfo Settings = new SettingsInfo
{
Enabled = true,
Permission = string.Empty,
Image = "https://i.imgur.com/zrGBH8Q.png",
Color = new IColor("#4B68FF"),
AnchorMin = "0 0", AnchorMax = "1 1",
OffsetMin = "20 -60", OffsetMax = "55 -25"
};
[JsonProperty(PropertyName = "Buttons Setting")]
public ButtonsSettings ButtonsSettings = new ButtonsSettings
{
Enabled = true,
Permission = string.Empty,
Commands = new[] {"panelsystem.buttons"},
Buttons = new List<ButtonInfo>
{
new ButtonInfo
{
Enabled = true,
Image = "https://i.imgur.com/RScZfCg.png",
Permission = string.Empty,
LangKey = "BtnShop",
Command = "chat.say /shop"
},
new ButtonInfo
{
Enabled = true,
Image = "https://i.imgur.com/84ixNM9.png",
Permission = string.Empty,
LangKey = "BtnStats",
Command = "chat.say /stats"
},
new ButtonInfo
{
Enabled = true,
Image = "https://i.imgur.com/hIyouiW.png",
Permission = string.Empty,
LangKey = "BtnBank",
Command = "chat.say /bank"
}
},
SettingsImage = "https://i.imgur.com/mJih52U.png",
Settings = new PanelSettings(true, string.Empty, true, false, true),
Size = new ButtonsSizeSettings(0, new List<ButtonsSizeSettings.ButtonPanelSize>
{
new ButtonsSizeSettings.ButtonPanelSize("sizeBig", string.Empty, 35, 16, 205, 25, 5),
new ButtonsSizeSettings.ButtonPanelSize("sizeMedium", string.Empty, 30, 14, 205, 25, 2.5f),
new ButtonsSizeSettings.ButtonPanelSize("sizeSmall", string.Empty, 25, 12, 205, 25, 0)
})
};
[JsonProperty(PropertyName = "Events", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<EventConf> Events = new List<EventConf>
{
new EventConf(true, "https://i.imgur.com/kxeEFQG.png", string.Empty, PanelType.CH47,
new PanelSettings(true, string.Empty, true, true, true),
new EventSizesInfo(0, new List<SizeData>(), new List<EventSizeData>
{
new EventSizeData("sizeBig", string.Empty, 25f),
new EventSizeData("sizeMedium", string.Empty, 20f),
new EventSizeData("sizeSmall", string.Empty, 15f)
}),
new ColorInfo(0, 2),
"CH47"),
new EventConf(true, "https://i.imgur.com/zjzI25y.png", string.Empty, PanelType.Bradley,
new PanelSettings(true, string.Empty, true, true, true),
new EventSizesInfo(0, new List<SizeData>(), new List<EventSizeData>
{
new EventSizeData("sizeBig", string.Empty, 25f),
new EventSizeData("sizeMedium", string.Empty, 20f),
new EventSizeData("sizeSmall", string.Empty, 15f)
}),
new ColorInfo(2, 3),
"Bradley"),
new EventConf(true, "https://i.imgur.com/PclffR9.png", string.Empty, PanelType.Helicopter,
new PanelSettings(true, string.Empty, true, true, true),
new EventSizesInfo(0, new List<SizeData>(), new List<EventSizeData>
{
new EventSizeData("sizeBig", string.Empty, 25f),
new EventSizeData("sizeMedium", string.Empty, 20f),
new EventSizeData("sizeSmall", string.Empty, 15f)
}),
new ColorInfo(3, 2),
"Helicopter"),
new EventConf(true, "https://i.imgur.com/NKNIqxz.png", string.Empty, PanelType.Airdrop,
new PanelSettings(true, string.Empty, true, true, true),
new EventSizesInfo(0, new List<SizeData>(), new List<EventSizeData>
{
new EventSizeData("sizeBig", string.Empty, 25f),
new EventSizeData("sizeMedium", string.Empty, 20f),
new EventSizeData("sizeSmall", string.Empty, 15f)
}),
new ColorInfo(0, 2),
"Airdrop"),
new EventConf(false, "https://i.imgur.com/lsEIGc5.png", string.Empty, PanelType.CargoShip,
new PanelSettings(true, string.Empty, true, true, true),
new EventSizesInfo(0, new List<SizeData>(), new List<EventSizeData>
{
new EventSizeData("sizeBig", string.Empty, 25f),
new EventSizeData("sizeMedium", string.Empty, 20f),
new EventSizeData("sizeSmall", string.Empty, 15f)
}),
new ColorInfo(0, 2),
"CargoShip"),
new EventConf(false, "https://i.imgur.com/Mkan1Ac.png", string.Empty, PanelType.Convoy,
new PanelSettings(true, string.Empty, true, true, true),
new EventSizesInfo(0, new List<SizeData>(), new List<EventSizeData>
{
new EventSizeData("sizeBig", string.Empty, 25f),
new EventSizeData("sizeMedium", string.Empty, 20f),
new EventSizeData("sizeSmall", string.Empty, 15f)
}),
new ColorInfo(0, 2),
"Convoy"),
new EventConf(false, "https://i.imgur.com/LkmaOCm.png", string.Empty, PanelType.ArmoredTrain,
new PanelSettings(true, string.Empty, true, true, true),
new EventSizesInfo(0, new List<SizeData>(), new List<EventSizeData>
{
new EventSizeData("sizeBig", string.Empty, 25f),
new EventSizeData("sizeMedium", string.Empty, 20f),
new EventSizeData("sizeSmall", string.Empty, 15f)
}),
new ColorInfo(0, 2),
"ArmoredTrain"),
new EventConf(false, "https://i.imgur.com/GbJGvQM.png", string.Empty, PanelType.WipeBlock,
new PanelSettings(true, string.Empty, true, true, true),
new EventSizesInfo(0, new List<SizeData>(), new List<EventSizeData>
{
new EventSizeData("sizeBig", string.Empty, 25f),
new EventSizeData("sizeMedium", string.Empty, 20f),
new EventSizeData("sizeSmall", string.Empty, 15f)
}),
new ColorInfo(0, 2),
"WipeBlock"),
new EventConf(false, "https://i.imgur.com/asflpJe.png", string.Empty, PanelType.Custom,
new PanelSettings(true, string.Empty, true, true, true),
new EventSizesInfo(0, new List<SizeData>(), new List<EventSizeData>
{
new EventSizeData("sizeBig", string.Empty, 25f),
new EventSizeData("sizeMedium", string.Empty, 20f),
new EventSizeData("sizeSmall", string.Empty, 15f)
}),
new ColorInfo(0, 2),
"UserNotify")
};
[JsonProperty(PropertyName = "Economics", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<EconomyConf> Economics = new List<EconomyConf>
{
new EconomyConf(1, true, "https://i.imgur.com/Q21Pwq9.png", "Economics", "Balance",
new PanelSettings(true, string.Empty, true, true, true),
new EconomyConf.EconomySizeSettings(0, new List<SizeData>(), new List<EconomyConf.EconomySize>
{
new EconomyConf.EconomySize("sizeBig", string.Empty, 20),
new EconomyConf.EconomySize("sizeMedium", string.Empty, 18),
new EconomyConf.EconomySize("sizeSmall", string.Empty, 16)
}),
6, string.Empty),
new EconomyConf(2, true, "https://i.imgur.com/DhSC8z0.png", "ServerRewards", "CheckPoints",
new PanelSettings(true, string.Empty, true, true, true),
new EconomyConf.EconomySizeSettings(0, new List<SizeData>(), new List<EconomyConf.EconomySize>
{
new EconomyConf.EconomySize("sizeBig", string.Empty, 20),
new EconomyConf.EconomySize("sizeMedium", string.Empty, 18),
new EconomyConf.EconomySize("sizeSmall", string.Empty, 16)
}),
7, string.Empty)
};
[JsonProperty(PropertyName = "Settings Interface")]
public SettingsInterface SettingsInterface = new SettingsInterface
{
IconsOnString = 4,
Size = 50,
Margin = 5
};
[JsonProperty(PropertyName = "Events Interface")]
public EventsInterface EventsInterface = new EventsInterface
{
SideIndent = 56,
UpIndent = 25,
Size = 35
};
[JsonProperty(PropertyName = "Hide Settings")]
public HideSettings Hide = new HideSettings
{
Enabled = false,
Command = "panel",
ShowLogo = true
};
[JsonProperty(PropertyName = "Colors", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<IColor> Colors = new List<IColor>
{
new IColor("#FFFFFF"),
new IColor("#000000"),
new IColor("#FFFFFF", 60),
new IColor("#FF6060"),
new IColor("#F6003B"),
new IColor("#0FF542"),
new IColor("#DCDCDC"),
new IColor("#4B68FF"),
new IColor("#0FF542"),
new IColor("#F68E00"),
new IColor("#F68E00"),
new IColor("#F6003B"),
new IColor("#F68E00"),
new IColor("#4B68FF"),
new IColor("#F68E00"),
new IColor("#0FF542"),
new IColor("#F68E00"),
new IColor("#0FF542"),
new IColor("#F68E00"),
new IColor("#F6003B"),
new IColor("#F6003B"),
new IColor("#F68E00"),
new IColor("#F68E00"),
new IColor("#0FF542"),
new IColor("#4B68FF")
};
}
private class HideSettings
{
[JsonProperty(PropertyName = "Enabled")]
public bool Enabled;
[JsonProperty(PropertyName = "Command")]
public string Command;
[JsonProperty(PropertyName = "Show Logo?")]
public bool ShowLogo;
}
private class EventsInterface
{
[JsonProperty(PropertyName = "Side Indent")]
public float SideIndent;
[JsonProperty(PropertyName = "Up Indent")]
public float UpIndent;
[JsonProperty(PropertyName = "Size")] public float Size;
}
private class SettingsInterface
{
[JsonProperty(PropertyName = "Icons On String")]
public int IconsOnString;
[JsonProperty(PropertyName = "Size")] public float Size;
[JsonProperty(PropertyName = "Margin")]
public float Margin;
}
private abstract class InterfacePosition
{
public string AnchorMin;
public string AnchorMax;
public string OffsetMin;
public string OffsetMax;
}
private class LogoSettings : InterfacePosition
{
[JsonProperty(PropertyName = "Enabled")]
public bool Enabled;
[JsonProperty(PropertyName = "Permission")]
public string Permission;
[JsonProperty(PropertyName = "Image")] public string Image;
[JsonProperty(PropertyName = "Command")]
public string Command;
public bool HasPermission(BasePlayer player)
{
return string.IsNullOrEmpty(Permission) || player.IPlayer.HasPermission(Permission);
}
}
private class ButtonsSettings
{
[JsonProperty(PropertyName = "Enabled")]
public bool Enabled;
[JsonProperty(PropertyName = "Permission")]
public string Permission;
[JsonProperty(PropertyName = "Commands for hiding/unhiding buttons",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public string[] Commands;
[JsonProperty(PropertyName = "Buttons", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<ButtonInfo> Buttons = new List<ButtonInfo>();
[JsonProperty(PropertyName = "Settings Image")]
public string SettingsImage;
[JsonProperty(PropertyName = "Panel Settngs")]
public PanelSettings Settings;
[JsonProperty(PropertyName = "Size Settings")]
public ButtonsSizeSettings Size;
public ButtonsSizeSettings.ButtonPanelSize GetPlayerSize(BasePlayer player, PanelType type)
{
var sizes = Size.Sizes;
int sizeIndex;
return PlayerData.GetOrAdd(player).Sizes.TryGetValue((int) type, out sizeIndex) &&
sizeIndex >= 0 &&
sizeIndex < sizes.Count
? sizes[sizeIndex]
: sizes[Size.DefaultSize];
}
public bool HasPermission(BasePlayer player)
{
return string.IsNullOrEmpty(Permission) || player.IPlayer.HasPermission(Permission);
}
}
private class ButtonInfo
{
[JsonProperty(PropertyName = "Enabled")]
public bool Enabled;
[JsonProperty(PropertyName = "Image")] public string Image;
[JsonProperty(PropertyName = "Permission")]
public string Permission;
[JsonProperty(PropertyName = "Lang Key (oxide/lang/**/PanelSystem.json)")]
public string LangKey;
[JsonProperty(PropertyName = "Command")]
public string Command;
[JsonIgnore] private int _id = -1;
[JsonIgnore]
public int ID
{
get
{
if (_id == -1)
{
_id = Random.Range(0, int.MaxValue);
_instance._buttonByID[_id] = this;
}
return _id;
}
}
public static ButtonInfo Get(int id)
{
ButtonInfo btn;
return _instance._buttonByID.TryGetValue(id, out btn) ? btn : null;
}
}
private class ButtonsSizeSettings : SizeSettings
{
public class ButtonPanelSize : SizeData
{
[JsonProperty(PropertyName = "Image Size")]
public float ImageSize;
[JsonProperty(PropertyName = "Font Size")]
public int FontSize;
[JsonProperty(PropertyName = "Up Indent")]
public float UpIndent;
[JsonProperty(PropertyName = "Side Indent")]
public int SideIndent;
[JsonProperty(PropertyName = "Margin")]
public float Margin;
public ButtonPanelSize(string langKey, string permission, float imageSize, int fontSize, float upIndent,
int sideIndent, float margin) : base(langKey, permission)
{
ImageSize = imageSize;
FontSize = fontSize;
UpIndent = upIndent;
SideIndent = sideIndent;
Margin = margin;
}
}
[JsonProperty(PropertyName = "Sizes", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<ButtonPanelSize> Sizes;
[JsonConstructor]
public ButtonsSizeSettings()
{
}
public ButtonsSizeSettings(int defaultSize, List<ButtonPanelSize> sizes2) : base(
defaultSize)
{
Sizes = sizes2;
}
}
private class SettingsInfo : InterfacePosition
{
[JsonProperty(PropertyName = "Enabled")]
public bool Enabled;
[JsonProperty(PropertyName = "Permission")]
public string Permission;
[JsonProperty(PropertyName = "Image")] public string Image;
[JsonProperty(PropertyName = "Color")] public IColor Color;
public bool HasPermission(BasePlayer player)
{
return string.IsNullOrEmpty(Permission) || player.IPlayer.HasPermission(Permission);
}
}
private class TimeSettings : InfoPanel
{
[JsonProperty(PropertyName = "Updating the time every minute of game time (may cause performance issues)")]
public bool GameUpdate;
}
private class InfoPanel
{
[JsonProperty(PropertyName = "Enabled")]
public bool Enabled;
[JsonProperty(PropertyName = "Permission")]
public string Permission;
[JsonProperty(PropertyName = "Image")] public string Image;
[JsonProperty(PropertyName = "Settings Image")]
public string SettingsImage;
[JsonProperty(PropertyName = "Panel Settngs")]
public PanelSettings Settings;
[JsonProperty(PropertyName = "Size Settings")]
public InfoPanelSizeSettings Size;
public InfoPanelSize GetPlayerSize(BasePlayer player, PanelType type)
{
var sizes = Size.Sizes;
int sizeIndex;
return PlayerData.GetOrAdd(player).Sizes.TryGetValue((int) type, out sizeIndex) &&
sizeIndex >= 0 &&
sizeIndex < sizes.Count
? sizes[sizeIndex]
: sizes[Size.DefaultSize];
}
public bool HasPermission(BasePlayer player)
{
return string.IsNullOrEmpty(Permission) || player.IPlayer.HasPermission(Permission);
}
public class InfoPanelSizeSettings : SizeSettings
{
[JsonProperty(PropertyName = "Sizes", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<InfoPanelSize> Sizes;
[JsonConstructor]
public InfoPanelSizeSettings()
{
}
public InfoPanelSizeSettings(int defaultSize, List<SizeData> sizes, List<InfoPanelSize> sizes2) : base(
defaultSize)
{
Sizes = sizes2;
}
}
public class InfoPanelSize : SizeData
{
[JsonProperty(PropertyName = "Image Size")]
public float ImageSize;
[JsonProperty(PropertyName = "Title Font Size")]
public int TitleFontSize;
[JsonProperty(PropertyName = "Value Font Size")]
public int ValueFontSize;
[JsonProperty(PropertyName = "Up Indent")]
public int UpIndent;
[JsonProperty(PropertyName = "Side Indent")]
public int SideIndent;
public InfoPanelSize(string langKey, string permission, float imageSize, int titleFontSize,
int valueFontSize, int upIndent, int sideIndent) : base(langKey, permission)
{
ImageSize = imageSize;
TitleFontSize = titleFontSize;
ValueFontSize = valueFontSize;
UpIndent = upIndent;
SideIndent = sideIndent;
}
}
}
private class PanelSettings
{
[JsonProperty(PropertyName = "Enabled")]
public bool Enabled;
[JsonProperty(PropertyName = "Permission")]
public string Permission;
[JsonProperty(PropertyName = "Switch")]
public bool Switch;
[JsonProperty(PropertyName = "Color")] public bool Color;
[JsonProperty(PropertyName = "Size")] public bool Size;
[JsonIgnore] public PanelType Type;
[JsonIgnore] public int ID;
[JsonIgnore] public string Image;
[JsonIgnore] public string Key;
[JsonIgnore] public List<SizeData> Sizes;
public PanelSettings(bool enabled, string permission,
bool @switch, bool color, bool size)
{
Enabled = enabled;
Permission = permission;
Switch = @switch;
Color = color;
Size = size;
}
public List<SizeData> GetSizes(BasePlayer player)
{
return Sizes.FindAll(x =>
string.IsNullOrEmpty(x.Permission) ||
_instance.permission.UserHasPermission(player.UserIDString, x.Permission));
}
}
private class SizeSettings
{
[JsonProperty(PropertyName = "Default Size (index)")]
public int DefaultSize;
[JsonConstructor]
protected SizeSettings()
{
}
protected SizeSettings(int defaultSize)
{
DefaultSize = defaultSize;
}
}
private class SizeData
{
[JsonProperty(PropertyName = "Lang Key")]
public string LangKey;
[JsonProperty(PropertyName = "Permission")]
public string Permission;
protected SizeData(string langKey, string permission)
{
LangKey = langKey;
Permission = permission;
}
}
private class EconomyConf
{
[JsonProperty(PropertyName = "ID")] public int ID;
[JsonProperty(PropertyName = "Enabled")]
public bool Enabled;
[JsonProperty(PropertyName = "Permission")]
public string Permission;
[JsonProperty(PropertyName = "Image")] public string Image;
[JsonProperty(PropertyName = "Plugin name")]
public string Plug;
[JsonProperty(PropertyName = "Balance show hook")]
public string BalanceHook;
[JsonProperty(PropertyName = "Panel Settngs")]
public PanelSettings Settings;
[JsonProperty(PropertyName = "Size Settings")]
public EconomySizeSettings Size;
[JsonProperty(PropertyName = "Default Color ID")]
public int ColorId;
public string GetColor()
{
return (ColorId >= 0 && ColorId < _instance._config.Colors.Count
? _instance._config.Colors[ColorId]
: _instance._config.Colors[0]).Get;
}
public double ShowBalance(BasePlayer player)
{
var plugin = _instance?.plugins?.Find(Plug);
if (plugin == null) return 0;
return Math.Round(Convert.ToDouble(plugin.Call(BalanceHook, player.userID)), 2);
}
public bool HasPermission(BasePlayer player)
{
return string.IsNullOrEmpty(Permission) || player.IPlayer.HasPermission(Permission);
}
public EconomyConf(int id, bool enabled, string image, string plug, string balanceHook,
PanelSettings settings, EconomySizeSettings size, int colorId, string permission)
{
ID = id;
Enabled = enabled;
Image = image;
Plug = plug;
BalanceHook = balanceHook;
Settings = settings;
Size = size;
ColorId = colorId;
Permission = permission;
}
public EconomySize GetPlayerSize(BasePlayer player)
{
var sizes = Size.Sizes;
int sizeIndex;
return PlayerData.GetOrAdd(player).EconomySizes.TryGetValue(ID, out sizeIndex) &&
sizeIndex >= 0 &&
sizeIndex < sizes.Count
? sizes[sizeIndex]
: sizes[Size.DefaultSize];
}
public class EconomySizeSettings : SizeSettings
{
[JsonProperty(PropertyName = "Sizes", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<EconomySize> Sizes;
[JsonConstructor]
public EconomySizeSettings()
{
}
public EconomySizeSettings(int defaultSize, List<SizeData> sizes, List<EconomySize> sizes2) : base(
defaultSize)
{
Sizes = sizes2;
}
}
public class EconomySize : SizeData
{
[JsonProperty(PropertyName = "Font Size")]
public int FontSize;
public EconomySize(string langKey, string permission, int fontSize) : base(langKey, permission)
{
FontSize = fontSize;
}
}
}
private class EventConf
{
[JsonProperty(PropertyName = "Enabled")]
public bool Enabled;
[JsonProperty(PropertyName = "Permission")]
public string Permission;
[JsonProperty(PropertyName = "Image")] public string Image;
[JsonProperty(PropertyName = "Type")] public PanelType Type;
[JsonProperty(PropertyName = "Panel Settings")]
public PanelSettings Settings;
[JsonProperty(PropertyName = "Size Settings")]
public EventSizesInfo Size;
[JsonProperty(PropertyName = "Color Settings")]
public ColorInfo Color;
[JsonProperty(PropertyName = "Key (MUST BE UNIQUE)")]
public string Key;
public string GetColor(BasePlayer player, bool status)
{
var data = PlayerData.GetOrAdd(player).GetColorData(Type, 0, Key);
var colorId = status ? data.ActiveId : data.InactiveId;
return _instance._config.Colors[Mathf.Min(colorId, _instance._config.Colors.Count - 1)].Get;
}
public EventConf(bool enabled, string image, string permission, PanelType type,
PanelSettings settings,
EventSizesInfo size,
ColorInfo color, string key)
{
Enabled = enabled;
Image = image;
Permission = permission;
Type = type;
Settings = settings;
Size = size;
Color = color;
Key = key;
}
public EventSizeData GetPlayerSize(BasePlayer player)
{
var sizes = Size.Sizes;
int sizeIndex;
switch (Type)
{
case PanelType.Custom:
{
return PlayerData.GetOrAdd(player).CustomSizes.TryGetValue(Key, out sizeIndex) &&
sizeIndex >= 0 &&
sizeIndex < sizes.Count
? sizes[sizeIndex]
: sizes[Size.DefaultSize];
}
default:
{
return PlayerData.GetOrAdd(player).Sizes.TryGetValue((int) Type, out sizeIndex) &&
sizeIndex >= 0 &&
sizeIndex < sizes.Count
? sizes[sizeIndex]
: sizes[Size.DefaultSize];
}
}
}
public bool HasPermission(BasePlayer player)
{
return string.IsNullOrEmpty(Permission) || player.IPlayer.HasPermission(Permission);
}
}
private class ColorInfo
{
[JsonProperty(PropertyName = "Default Active Color ID")]
public int DefaultActiveColorId;
[JsonProperty(PropertyName = "Default Inactive Color ID")]
public int DefaultInactiveColorId;
public ColorInfo(int defaultActiveColorId, int defaultInactiveColorId)
{
DefaultActiveColorId = defaultActiveColorId;
DefaultInactiveColorId = defaultInactiveColorId;
}
}
private class EventSizesInfo : SizeSettings
{
[JsonProperty(PropertyName = "Sizes", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<EventSizeData> Sizes;
[JsonConstructor]
public EventSizesInfo()
{
}
public EventSizesInfo(int defaultSize, List<SizeData> sizes, List<EventSizeData> sizes2) : base(defaultSize)
{
Sizes = sizes2;
}
}
private class EventSizeData : SizeData
{
[JsonProperty(PropertyName = "Size")] public float Size;
public EventSizeData(string langKey, string permission, float size) : base(langKey, permission)
{
Size = size;
}
}
private class IColor
{
[JsonProperty(PropertyName = "HEX")] public string Hex;
[JsonProperty(PropertyName = "Opacity (0 - 100)")]
public readonly float Alpha;
[JsonIgnore] private string _color;
[JsonIgnore]
public string Get
{
get
{
if (string.IsNullOrEmpty(_color))
_color = GetColor();
return _color;
}
}
private string GetColor()
{
if (string.IsNullOrEmpty(Hex)) Hex = "#FFFFFF";
var str = Hex.Trim('#');
if (str.Length != 6) 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);
return $"{(double) r / 255} {(double) g / 255} {(double) b / 255} {Alpha / 100}";
}
public IColor()
{
}
public IColor(string hex, float alpha = 100)
{
Hex = hex;
Alpha = alpha;
}
}
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 Data
private PluginData _data;
private void SaveData()
{
Interface.Oxide.DataFileSystem.WriteObject($"{Name}/Main", _data);
}
private void LoadData()
{
try
{
_data = Interface.Oxide.DataFileSystem.ReadObject<PluginData>($"{Name}/Main");
}
catch (Exception e)
{
PrintError(e.ToString());
}
if (_data == null) _data = new PluginData();
}
private class PluginData
{
[JsonProperty(PropertyName = "Players", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<ulong, PlayerData> Players = new Dictionary<ulong, PlayerData>();
}
private class PlayerData
{
#region Mode
[JsonProperty(PropertyName = "Hided")] public bool Hided;
public bool IsHided()
{
return Hided;
}
public void ChangeHide(bool newHide)
{
Hided = newHide;
}
#endregion
#region Sizes
[JsonProperty(PropertyName = "Sizes", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<int, int> Sizes = new Dictionary<int, int>(); //type - size index
[JsonProperty(PropertyName = "Economy Sizes", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<int, int> EconomySizes = new Dictionary<int, int>(); //type - size index
[JsonProperty(PropertyName = "Custom Sizes", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, int> CustomSizes = new Dictionary<string, int>(); //key - size index
public void SetSize(PanelType type, int id, string key, int index)
{
switch (type)
{
case PanelType.Economics:
{
EconomySizes[id] = index;
break;
}
case PanelType.Custom:
{
if (string.IsNullOrEmpty(key)) return;
CustomSizes[key] = index;
break;
}
default:
{
Sizes[(int) type] = index;
break;
}
}
}
public SizeData GetSize(BasePlayer player, PanelType type, int id, string key)
{
switch (type)
{
case PanelType.Economics:
return _instance._config.Economics.Find(x => x.ID == id)?.GetPlayerSize(player);
case PanelType.Online:
return _instance._config.Players.GetPlayerSize(player, type);
case PanelType.Time:
return _instance._config.Time.GetPlayerSize(player, type);
case PanelType.Button:
return _instance._config.ButtonsSettings.GetPlayerSize(player, type);
case PanelType.Custom:
return _instance._config.Events.Find(x => x.Type == type && x.Key == key)
?.GetPlayerSize(player);
default: //events
return _instance._config.Events.Find(x => x.Type == type)?.GetPlayerSize(player);
}
}
#endregion
#region Colors
[JsonProperty(PropertyName = "Events Colors", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<int, ColorData> EventsColors = new Dictionary<int, ColorData>();
[JsonProperty(PropertyName = "Custom Colors", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<string, ColorData> CustomColors = new Dictionary<string, ColorData>();
[JsonProperty(PropertyName = "Economics Colors", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public Dictionary<int, ColorData> EconomicsColors = new Dictionary<int, ColorData>();
public ColorData GetColorData(PanelType type, int id, string key)
{
switch (type)
{
case PanelType.Custom:
{
ColorData colorData;
if (!CustomColors.TryGetValue(key, out colorData))
CustomColors.Add(key, colorData = new ColorData(type, key));
return colorData;
}
case PanelType.Economics:
{
ColorData colorData;
if (!EconomicsColors.TryGetValue(id, out colorData))
EconomicsColors.Add(id, colorData = new ColorData(id));
return colorData;
}
default:
{
ColorData colorData;
if (!EventsColors.TryGetValue((int) type, out colorData))
EventsColors.Add((int) type, colorData = new ColorData(type, key));
return colorData;
}
}
}
public void SetEventColor(PanelType type, int id, string key, int colorPage, int index)
{
var colorData = GetColorData(type, id, key);
switch (type)
{
case PanelType.Economics:
{
colorData.ActiveId = index;
break;
}
default:
{
switch (colorPage)
{
case 0:
{
colorData.ActiveId = index;
break;
}
case 1:
{
colorData.InactiveId = index;
break;
}
}
break;
}
}
}
#endregion
#region Hidden Panels
[JsonProperty(PropertyName = "Hidden Panel", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<int> HiddenPanel = new List<int>();
[JsonProperty(PropertyName = "Hidden Economy", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<int> HiddenEconomy = new List<int>();
public void SwitchHidden(PanelType type, int id)
{
switch (type)
{
case PanelType.Economics:
{
if (HiddenEconomy.Contains(id))
HiddenEconomy.Remove(id);
else
HiddenEconomy.Add(id);
break;
}
default:
{
var typeId = (int) type;
if (HiddenPanel.Contains(typeId))
HiddenPanel.Remove(typeId);
else
HiddenPanel.Add(typeId);
break;
}
}
}
public bool IsHidden(PanelType type, int id = 0)
{
switch (type)
{
case PanelType.Economics:
return HiddenEconomy.Contains(id);
default:
return HiddenPanel.Contains((int) type);
}
}
#endregion
public static PlayerData GetOrAdd(BasePlayer player)
{
if (!_instance._data.Players.ContainsKey(player.userID))
_instance._data.Players.Add(player.userID, new PlayerData());
return _instance._data.Players[player.userID];
}
}
private class ColorData
{
[JsonProperty(PropertyName = "Active ID")]
public int ActiveId;
[JsonProperty(PropertyName = "Inactive ID")]
public int InactiveId;
[JsonConstructor]
public ColorData(int activeId, int inactiveId)
{
ActiveId = activeId;
InactiveId = inactiveId;
}
public ColorData(PanelType type, string key)
{
var mainColor = _instance._config.Events.Find(x => x.Type == type && x.Key == key)?.Color;
ActiveId = mainColor?.DefaultActiveColorId ?? 0;
InactiveId = mainColor?.DefaultInactiveColorId ?? 0;
}
public ColorData(int id)
{
var mainColor = _instance._config.Economics.Find(x => x.ID == id)?.ColorId;
ActiveId = mainColor ?? 0;
InactiveId = mainColor ?? 0;
}
}
#endregion
#region Hooks
private void Init()
{
_instance = this;
LoadData();
RegisterPermissions();
RegisterCommands();
}
private void OnServerInitialized()
{
LoadSettings();
LoadImages();
LoadEvents();
foreach (var player in BasePlayer.activePlayerList) OnPlayerConnected(player);
timer.Every(5, StartUpdate);
if (_config.Time.Enabled && _config.Time.GameUpdate)
GetTimeComponent();
}
private void OnServerSave()
{
timer.In(Random.Range(2f, 7f), SaveData);
}
private void Unload()
{
SaveData();
foreach (var player in BasePlayer.activePlayerList)
{
CuiHelper.DestroyUi(player, Layer);
CuiHelper.DestroyUi(player, EventsLayer);
CuiHelper.DestroyUi(player, SettingsLayer);
}
if (_config.Time.Enabled && _config.Time.GameUpdate &&
timeComponent != null)
timeComponent.OnMinute -= OnMinute;
_instance = null;
}
private void OnPlayerConnected(BasePlayer player)
{
MainUi(player, true);
EventsUi(player, true);
}
#region Events
#region Convoy
private void OnConvoyStart()
{
OnEventChangeStatus(PanelType.Convoy, true);
}
private void OnConvoyStop()
{
OnEventChangeStatus(PanelType.Convoy, false);
}
#endregion
#region Armored Train
private void OnArmoredTrainEventStart()
{
OnEventChangeStatus(PanelType.ArmoredTrain, true);
}
private void OnArmoredTrainEventStop()
{
OnEventChangeStatus(PanelType.ArmoredTrain, false);
}
#endregion
#region WipeBlock
private void OnWipeBlockEnded()
{
OnEventChangeStatus(PanelType.WipeBlock, false);
}
#endregion
private void OnEntitySpawned(BaseEntity entity)
{
OnEntityChangeStatus(entity, true);
}
private void OnEntityKill(BaseEntity entity)
{
OnEntityChangeStatus(entity, false);
}
#endregion
#endregion
#region Commands
[ConsoleCommand("UI_PanelSystem")]
private void CmdConsole(ConsoleSystem.Arg arg)
{
var player = arg.Player();
if (player == null || !arg.HasArgs()) return;
switch (arg.Args[0])
{
case "settings":
{
int panelId = -1, id = 0, page = 0, colorPage = -1;
if (arg.HasArgs(2))
int.TryParse(arg.Args[1], out panelId);
if (arg.HasArgs(3))
int.TryParse(arg.Args[2], out id);
if (arg.HasArgs(4)) int.TryParse(arg.Args[3], out page);
if (arg.HasArgs(5))
int.TryParse(arg.Args[4], out colorPage);
var key = string.Empty;
if (arg.HasArgs(6))
key = arg.Args[5];
SettingsUi(player, panelId, id, page, colorPage, key);
break;
}
case "setsize":
{
PanelType type;
int panelId, id, page, newIndex;
if (!arg.HasArgs(6) ||
!int.TryParse(arg.Args[1], out panelId) ||
!int.TryParse(arg.Args[2], out id) ||
!int.TryParse(arg.Args[3], out page) ||
!Enum.TryParse(arg.Args[4], out type) ||
!int.TryParse(arg.Args[5], out newIndex))
return;
var key = string.Empty;
if (arg.HasArgs(7))
key = arg.Args[6];
PlayerData.GetOrAdd(player).SetSize(type, id, key, newIndex);
MainUi(player);
EventsUi(player);
SettingsUi(player, panelId, id, page);
break;
}
case "setcolor":
{
PanelType type;
int panelId, id, page, colorPage, selIndex;
if (!arg.HasArgs(7) ||
!int.TryParse(arg.Args[1], out panelId) ||
!int.TryParse(arg.Args[2], out id) ||
!int.TryParse(arg.Args[3], out page) ||
!int.TryParse(arg.Args[4], out colorPage) ||
!Enum.TryParse(arg.Args[5], out type) ||
!int.TryParse(arg.Args[6], out selIndex))
return;
var key = string.Empty;
if (arg.HasArgs(8))
key = arg.Args[7];
PlayerData.GetOrAdd(player).SetEventColor(type, id, key, colorPage, selIndex);
MainUi(player);
EventsUi(player);
switch ((PanelType) panelId)
{
case PanelType.Economics:
{
SettingsUi(player, panelId, id);
break;
}
default:
{
SettingsUi(player, panelId, id, page);
break;
}
}
break;
}
case "switch":
{
PanelType type;
int panelId, id;
if (!arg.HasArgs(4) ||
!int.TryParse(arg.Args[1], out panelId) ||
!int.TryParse(arg.Args[2], out id) ||
!Enum.TryParse(arg.Args[3], out type))
return;
PlayerData.GetOrAdd(player).SwitchHidden(type, id);
MainUi(player);
EventsUi(player);
SettingsUi(player, panelId, id);
break;
}
}
}
[ConsoleCommand("panelsystem.sendcmd")]
private void CmdSendCommand(ConsoleSystem.Arg arg)
{
var player = arg.Player();
if (player == null || !arg.HasArgs()) return;
player.SendConsoleCommand(
arg.Args[0] == _chatSay
? $"{arg.Args[0]} \" {string.Join(" ", arg.Args.ToList().GetRange(1, arg.Args.Length - 1))}\" 0"
: string.Join(" ", arg.Args));
}
private void CmdSwitchButtons(IPlayer cov, string command, string[] args)
{
var player = cov?.Object as BasePlayer;
if (player == null) return;
PlayerData.GetOrAdd(player).SwitchHidden(PanelType.Button, 0);
MainUi(player);
}
private void CmdHide(IPlayer cov, string command, string[] args)
{
var player = cov?.Object as BasePlayer;
if (player == null) return;
if (args.Length < 1)
{
Reply(player, $"Error syntax! Use: /{command} hide/show");
return;
}
var data = PlayerData.GetOrAdd(player);
switch (args[0].ToLower())
{
case "hide":
{
data.ChangeHide(true);
break;
}
case "show":
{
data.ChangeHide(false);
break;
}
default:
{
Reply(player, $"Error syntax! Use: /{command} hide/show");
return;
}
}
MainUi(player, true);
EventsUi(player, true);
}
#endregion
#region Interface
private void MainUi(BasePlayer player, bool first = false)
{
var data = PlayerData.GetOrAdd(player);
var hided = data.IsHided();
var container = new CuiElementContainer();
#region Background
if (first)
{
CuiHelper.DestroyUi(player, Layer + ".Background");
container.Add(new CuiPanel
{
RectTransform =
{
AnchorMin = "1 1", AnchorMax = "1 1",
OffsetMin = "0 0", OffsetMax = "0 0"
},
Image = {Color = "0 0 0 0"}
}, _config.DisplayType, Layer + ".Background");
}
container.Add(new CuiPanel
{
RectTransform =
{
AnchorMin = "1 1", AnchorMax = "1 1",
OffsetMin = "0 0", OffsetMax = "0 0"
},
Image = {Color = "0 0 0 0"}
}, Layer + ".Background", Layer);
#endregion
#region Main
#region Logo
if (((hided && _config.Hide.ShowLogo) || hided == false) && _config.Logo.Enabled &&
_config.Logo.HasPermission(player))
{
container.Add(new CuiElement
{
Name = Layer + ".Logotype",
Parent = Layer,
Components =
{
new CuiRawImageComponent {Png = ImageLibrary.Call<string>("GetImage", _config.Logo.Image)},
new CuiRectTransformComponent
{
AnchorMin = _config.Logo.AnchorMin,
AnchorMax = _config.Logo.AnchorMax,
OffsetMin = _config.Logo.OffsetMin,
OffsetMax = _config.Logo.OffsetMax
}
}
});
if (!string.IsNullOrEmpty(_config.Logo.Command))
container.Add(new CuiButton
{
RectTransform = {AnchorMin = "0 0", AnchorMax = "1 1"},
Text = {Text = ""},
Button =
{
Command = $"panelsystem.sendcmd {_config.Logo.Command}",
Color = "0 0 0 0"
}
}, Layer + ".Logotype");
}
#endregion
#region Panels
var heightSwitch = 0f;
#region Players
if (hided == false && _config.Players.Enabled && !data.IsHidden(PanelType.Online) &&
_config.Players.HasPermission(player))
{
var panel = _config.Players;
var size = panel.GetPlayerSize(player, PanelType.Online);
heightSwitch -= size.UpIndent;
container.Add(new CuiPanel
{
RectTransform =
{
AnchorMin = "0 0", AnchorMax = "1 1",
OffsetMin = $"-{size.SideIndent} {heightSwitch}",
OffsetMax = $"-{size.SideIndent} {heightSwitch}"
},
Image = {Color = "0 0 0 0"}
}, Layer, Layer + $".Update.{PanelType.Online}");
container.AddRange(UpdateUi(player, PanelType.Online, true));
}
#endregion
#region Online
if (hided == false && _config.Time.Enabled && !data.IsHidden(PanelType.Time) &&
_config.Time.HasPermission(player))
{
var panel = _config.Time;
var size = panel.GetPlayerSize(player, PanelType.Time);
heightSwitch -= size.UpIndent;
container.Add(new CuiPanel
{
RectTransform =
{
AnchorMin = "0 0", AnchorMax = "1 1",
OffsetMin = $"-{size.SideIndent} {heightSwitch}",
OffsetMax = $"-{size.SideIndent} {heightSwitch}"
},
Image = {Color = "0 0 0 0"}
}, Layer, Layer + $".Update.{PanelType.Time}");
container.AddRange(UpdateUi(player, PanelType.Time, true));
}
#endregion
#endregion
#region Economics
if (hided == false)
container.AddRange(UpdateUi(player, PanelType.Economics, true));
#endregion
#region Buttons
if (hided == false && _config.ButtonsSettings.Enabled && _config.ButtonsSettings.HasPermission(player))
{
var size = _config.ButtonsSettings.GetPlayerSize(player, PanelType.Button);
var ySwitch = -size.UpIndent;
var height = size.ImageSize;
var buttons = GetAvailableButtons(player);
buttons.ForEach(btn =>
{
if (PlayerData.GetOrAdd(player).IsHidden(PanelType.Button)) return;
var title = Msg(player, btn.LangKey);
var fontSize = size.FontSize;
var length = (title.Length + 1) * fontSize * 0.5f;
container.Add(new CuiPanel
{
RectTransform =
{
AnchorMin = "0 0", AnchorMax = "1 1",
OffsetMin = $"-{size.SideIndent} {ySwitch - height}",
OffsetMax = $"-{size.SideIndent} {ySwitch}"
},
Image =
{
Color = "0 0 0 0"
}
}, Layer, Layer + $".Buttons.{btn.ID}");
if (!string.IsNullOrEmpty(btn.Image))
container.Add(new CuiElement
{
Parent = Layer + $".Buttons.{btn.ID}",
Components =
{
new CuiRawImageComponent
{
Png = ImageLibrary.Call<string>("GetImage", btn.Image)
},
new CuiRectTransformComponent
{
AnchorMin = "1 0", AnchorMax = "1 1",
OffsetMin = $"-{height} 0",
OffsetMax = "0 0"
}
}
});
container.Add(new CuiLabel
{
RectTransform =
{
AnchorMin = "1 0", AnchorMax = "1 1",
OffsetMin = $"-{height + 5 + length + fontSize + 5} 0",
OffsetMax = $"-{height + 5} 0"
},
Text =
{
Text = title,
Align = TextAnchor.MiddleRight,
Font = "robotocondensed-regular.ttf",
FontSize = fontSize,
Color = "1 1 1 1"
}
}, Layer + $".Buttons.{btn.ID}");
container.Add(new CuiButton
{
RectTransform =
{
AnchorMin = "0 0", AnchorMax = "1 1",
OffsetMin = $"-{height + 10 + length + fontSize + 5} 0",
OffsetMax = "0 0"
},
Text =
{
Text = ""
},
Button =
{
Command = $"panelsystem.sendcmd {btn.Command}",
Color = "0 0 0 0"
}
}, Layer + $".Buttons.{btn.ID}");
ySwitch = ySwitch - height - size.Margin;
});
}
#endregion
#endregion
CuiHelper.DestroyUi(player, Layer);
CuiHelper.AddUi(player, container);
}
private void EventsUi(BasePlayer player, bool first = false)
{
var data = PlayerData.GetOrAdd(player);
var hided = data.IsHided();
var container = new CuiElementContainer();
#region Background
if (first)
{
CuiHelper.DestroyUi(player, EventsLayer + ".Background");
container.Add(new CuiPanel
{
RectTransform =
{
AnchorMin = "0 1", AnchorMax = "0 1",
OffsetMin = "0 0", OffsetMax = "0 0"
},
Image = {Color = "0 0 0 0"}
}, _config.DisplayType, EventsLayer + ".Background");
}
container.Add(new CuiPanel
{
RectTransform =
{
AnchorMin = "0 0", AnchorMax = "1 1"
},
Image = {Color = "0 0 0 0"}
}, EventsLayer + ".Background", EventsLayer);
#endregion
#region Main
#region Settings
if (hided == false && _config.Settings.Enabled && _config.Settings.HasPermission(player))
{
container.Add(new CuiPanel
{
RectTransform =
{
AnchorMin = _config.Settings.AnchorMin,
AnchorMax = _config.Settings.AnchorMax,
OffsetMin = _config.Settings.OffsetMin,
OffsetMax = _config.Settings.OffsetMax
},
Image =
{
Color = _config.Settings.Color.Get
}
}, EventsLayer,
// ... [Code preview truncated due to length. Full source in attached file] ...