Advertisement
battlevox

lustymap.cs

Dec 5th, 2016
285
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 103.20 KB | None | 0 0
  1. // Reference: System.Drawing
  2. using System;
  3. using System.Text;
  4. using System.Collections.Generic;
  5. using Newtonsoft.Json.Linq;
  6. using Oxide.Core;
  7. using Oxide.Core.Configuration;
  8. using Oxide.Game.Rust.Cui;
  9. using Oxide.Core.Plugins;
  10. using UnityEngine;
  11. using System.Linq;
  12. using System.Globalization;
  13. using System.IO;
  14. using System.Collections;
  15. using System.Drawing;
  16.  
  17. namespace Oxide.Plugins
  18. {
  19.     [Info("LustyMap", "Kayzor / k1lly0u", "2.0.66", ResourceId = 1333)]
  20.     class LustyMap : RustPlugin
  21.     {
  22.         #region Fields
  23.         [PluginReference] Plugin EventManager;
  24.         [PluginReference] Plugin Friends;
  25.         [PluginReference] Plugin Clans;
  26.  
  27.         static GameObject webObject;
  28.         static ImageAssets assets;
  29.         static GameObject mapObject;
  30.         static MapSplitter mapSplitter;
  31.  
  32.         static LustyMap instance;
  33.         static float mapSize;
  34.         static string mapSeed;
  35.         static string worldSize;
  36.         static string level;
  37.  
  38.         private bool activated;
  39.         private bool isNewSave;
  40.         private bool isRustFriends;
  41.        
  42.         ImageStore storedImages;
  43.         private DynamicConfigFile imageData;
  44.  
  45.         MarkerData storedMarkers;
  46.         private DynamicConfigFile markerData;
  47.  
  48.         private Dictionary<string, MapUser> mapUsers;
  49.  
  50.         private List<MapMarker> staticMarkers;
  51.         private Dictionary<string, MapMarker> customMarkers;
  52.         private Dictionary<uint, ActiveEntity> temporaryMarkers;
  53.  
  54.         private Dictionary<string, List<string>> clanData;
  55.  
  56.         static string dataDirectory = $"file://{Interface.Oxide.DataDirectory}{Path.DirectorySeparatorChar}LustyMap{Path.DirectorySeparatorChar}";
  57.         #endregion
  58.        
  59.         #region User Class  
  60.         class MapUser : MonoBehaviour
  61.         {
  62.             private Dictionary<string, List<string>> friends;
  63.             public HashSet<string> friendList;
  64.  
  65.             public BasePlayer player;
  66.             public MapMode mode;
  67.             public MapMode lastMode;
  68.  
  69.             private MapMarker marker;
  70.  
  71.             private int mapX;
  72.             private int mapZ;
  73.  
  74.             private int currentX;
  75.             private int currentZ;
  76.             private int mapZoom;
  77.  
  78.             private bool mapOpen;
  79.             private bool inEvent;
  80.             private bool adminMode;
  81.  
  82.             private int changeCount;
  83.             private double lastChange;
  84.             private bool isBlocked;
  85.  
  86.             private SpamOptions spam;
  87.  
  88.             private bool afkDisabled;
  89.             private int lastMoveTime;
  90.             private float lastX;
  91.             private float lastZ;
  92.  
  93.  
  94.             void Awake()
  95.             {                
  96.                 player = GetComponent<BasePlayer>();
  97.                 friends = new Dictionary<string, List<string>>
  98.                 {
  99.                     {"Clans", new List<string>() },
  100.                     {"FriendsAPI", new List<string>() }
  101.                 };
  102.                 friendList = new HashSet<string>();
  103.                 spam = instance.configData.SpamOptions;            
  104.                 inEvent = false;
  105.                 mapOpen = false;
  106.                 afkDisabled = false;
  107.                 mode = MapMode.None;
  108.                 lastMode = MapMode.None;
  109.                 adminMode = false;
  110.                 InvokeRepeating("UpdateMarker", 0.1f, 1f);
  111.             }
  112.             void OnDestroy()
  113.             {
  114.                 CancelInvoke();
  115.                 DestroyUI();
  116.             }
  117.             public void InitializeComponent()
  118.             {
  119.                 if (MapSettings.friends)
  120.                 {
  121.                     FillFriendList();
  122.                 }
  123.                 if (MapSettings.forcedzoom && MapSettings.complexmap)
  124.                 {
  125.                     if (!instance.configData.MapOptions.StartOpen)
  126.                         ToggleMapType(MapMode.None);
  127.                     else
  128.                     {
  129.                         mapZoom = MapSettings.zoomlevel;
  130.                         ToggleMapType(MapMode.Complex);
  131.                     }
  132.                 }
  133.                 else if (MapSettings.minimap)
  134.                 {
  135.                     if (!instance.configData.MapOptions.StartOpen)
  136.                         ToggleMapType(MapMode.None);
  137.                     else
  138.                     {
  139.                         mode = MapMode.Minimap;
  140.                         ToggleMapType(mode);
  141.                     }
  142.                 }                
  143.             }
  144.  
  145.             #region Friends
  146.             private void FillFriendList()
  147.             {                
  148.                 if (instance.configData.FriendOptions.UseClans)
  149.                 {
  150.                     var clanTag = instance.GetClan(player.userID);
  151.                     if (!string.IsNullOrEmpty(clanTag) && instance.clanData.ContainsKey(clanTag))                    
  152.                         friends["Clans"] = instance.clanData[clanTag];
  153.                 }
  154.                    
  155.                 if (instance.configData.FriendOptions.UseFriends)
  156.                     friends["FriendsAPI"] = instance.GetFriends(player.userID);
  157.                 UpdateMembers();
  158.             }
  159.             private void UpdateMembers()
  160.             {
  161.                 friendList.Clear();
  162.                 foreach (var list in friends)
  163.                 {
  164.                     foreach (var member in list.Value)
  165.                         friendList.Add(member);
  166.                 }
  167.             }
  168.             #endregion
  169.  
  170.             #region Maps
  171.             public float Rotation() => GetDirection(player?.transform?.rotation.eulerAngles.y ?? 0);
  172.             public int Position(bool x) => x ? mapX : mapZ;            
  173.             public void Position(bool x, int pos)
  174.             {
  175.                 if (x) mapX = pos;
  176.                 else mapZ = pos;
  177.             }
  178.                        
  179.             public void ToggleMapType(MapMode mapMode)
  180.             {
  181.                 if (isBlocked || IsSpam()) return;
  182.  
  183.                 DestroyUI();              
  184.  
  185.                 if (mapMode == MapMode.None)
  186.                 {
  187.                     CancelInvoke("UpdateMap");                  
  188.                     mode = MapMode.None;
  189.                     mapOpen = false;
  190.  
  191.                     if (MapSettings.minimap)                    
  192.                         instance.CreateShrunkUI(player);                    
  193.                 }
  194.                 else
  195.                 {              
  196.                     mapOpen = true;
  197.                     switch (mapMode)
  198.                     {
  199.                         case MapMode.Main:
  200.                             mode = MapMode.Main;
  201.                             instance.OpenMainMap(player);
  202.                             break;
  203.                         case MapMode.Complex:
  204.                             mode = MapMode.Complex;                            
  205.                             instance.OpenComplexMap(player);
  206.                             break;
  207.                         case MapMode.Minimap:
  208.                             mode = MapMode.Minimap;
  209.                             instance.OpenMiniMap(player);
  210.                             break;
  211.                     }
  212.                     if (!IsInvoking("UpdateMap"))                    
  213.                         InvokeRepeating("UpdateMap", 0.1f, instance.configData.MapOptions.UpdateSpeed);                    
  214.                 }    
  215.             }                      
  216.             public void UpdateMap()
  217.             {                
  218.                 switch (mode)
  219.                 {
  220.                     case MapMode.None:
  221.                         break;
  222.                     case MapMode.Main:
  223.                         instance.UpdateOverlay(player, LustyUI.MainOverlay, LustyUI.MainMin, LustyUI.MainMax, 0.01f);
  224.                         break;
  225.                     case MapMode.Complex:
  226.                         CheckForChange();
  227.                         instance.UpdateCompOverlay(player);
  228.                         break;
  229.                     case MapMode.Minimap:
  230.                         instance.UpdateOverlay(player, LustyUI.MiniOverlay, LustyUI.MiniMin, LustyUI.MiniMax, 0.03f);
  231.                         break;
  232.                 }
  233.             }
  234.             #endregion
  235.  
  236.             #region Complex
  237.             public bool HasMapOpen() => (mapOpen && mode == MapMode.Main);
  238.             public int Zoom() => mapZoom;
  239.             public void Zoom(bool zoomIn)
  240.             {
  241.                 var zoom = mapZoom;
  242.                 if (zoomIn)
  243.                 {
  244.                     if (zoom < 3)
  245.                         zoom++;
  246.                     else return;
  247.                 }
  248.                 else
  249.                 {
  250.                     if (zoom > 0)
  251.                         zoom--;
  252.                     else return;
  253.                 }
  254.                 if (IsInvoking("UpdateMap"))
  255.                     CancelInvoke("UpdateMap");
  256.                 SwitchZoom(zoom);
  257.             }
  258.             private void SwitchZoom(int zoom)
  259.             {
  260.                 if (zoom == 0 && MapSettings.minimap)
  261.                 {                    
  262.                     mapZoom = zoom;
  263.                     ToggleMapType(MapMode.Minimap);
  264.                 }
  265.                 else
  266.                 {
  267.                     if (zoom == 0 && !MapSettings.minimap)
  268.                         zoom = 1;
  269.                    
  270.                     mapZoom = zoom;
  271.                     currentX = 0;
  272.                     currentZ = 0;
  273.                     ToggleMapType(MapMode.Complex);
  274.                 }
  275.             }
  276.             public int Current(bool x) => x ? currentX : currentZ;
  277.             public void Current(bool x, int num)
  278.             {
  279.                 if (x) currentX = num;
  280.                 else currentZ = num;
  281.             }          
  282.             private void CheckForChange()
  283.             {
  284.                 var mapSlices = ZoomToCount(mapZoom);
  285.                 float x = player.transform.position.x + mapSize / 2f;
  286.                 float z = player.transform.position.z + mapSize / 2f;
  287.                 var mapres = mapSize / mapSlices;
  288.  
  289.                 var newX = Convert.ToInt32(Math.Ceiling(x / mapres)) - 1;
  290.                 var newZ = mapSlices - Convert.ToInt32(Math.Ceiling(z / mapres));
  291.  
  292.                 if (currentX != newX || currentZ != newZ)
  293.                 {
  294.                     DestroyUI();
  295.                     currentX = newX;
  296.                     currentZ = newZ;
  297.                     var container = LustyUI.StaticComplex[mapZoom][newX, newZ];
  298.                     instance.OpenComplexMap(player);
  299.                 }
  300.             }
  301.             #endregion
  302.  
  303.             #region Spam Checking
  304.             private bool IsSpam()
  305.             {
  306.                 if (!spam.Enabled) return false;
  307.  
  308.                 changeCount++;
  309.                 var current = GrabCurrentTime();
  310.                 if (current - lastChange < spam.TimeBetweenAttempts)
  311.                 {
  312.                     lastChange = current;
  313.                     if (changeCount > spam.WarningAttempts && changeCount < spam.DisableAttempts)
  314.                     {
  315.                         instance.SendReply(player, instance.msg("spamWarning", player.UserIDString));
  316.                         return false;
  317.                     }
  318.                     if (changeCount >= spam.DisableAttempts)
  319.                     {
  320.                         instance.SendReply(player, string.Format(instance.msg("spamDisable", player.UserIDString), spam.DisableSeconds));
  321.                         Block();
  322.                         Invoke("Unblock", spam.DisableSeconds);
  323.                         return true;
  324.                     }
  325.                 }
  326.                 else
  327.                 {
  328.                     lastChange = current;
  329.                     changeCount = 0;
  330.                 }
  331.                 return false;
  332.             }
  333.             private void Block()
  334.             {                
  335.                 isBlocked = true;
  336.                 OnDestroy();
  337.             }
  338.             private void Unblock()
  339.             {
  340.                 isBlocked = false;
  341.                 ToggleMapType(lastMode);
  342.                 instance.SendReply(player, instance.msg("spamEnable", player.UserIDString));
  343.             }
  344.             #endregion
  345.  
  346.             #region Other
  347.             public bool InEvent() => inEvent;
  348.             public bool IsAdmin() => adminMode;
  349.             public void ToggleEvent(bool isPlaying) => inEvent = isPlaying;
  350.             public void ToggleAdmin(bool enabled) => adminMode = enabled;
  351.             public void DestroyUI() => LustyUI.DestroyUI(player);
  352.             private void UpdateMarker()
  353.             {
  354.                 var currentX = (float)Math.Round(transform.position.x, 1);
  355.                 var currentZ = (float)Math.Round(transform.position.z, 1);
  356.  
  357.                 marker = new MapMarker { name = RemoveSpecialCharacters(player.displayName), r = GetDirection(player?.eyes?.rotation.eulerAngles.y ?? 0), x = GetPosition(transform.position.x), z = GetPosition(transform.position.z) };
  358.  
  359.                 if (lastX == currentX && lastZ == currentZ)
  360.                     ++lastMoveTime;
  361.                 else
  362.                 {
  363.                     lastX = currentX;
  364.                     lastZ = currentZ;
  365.                     lastMoveTime = 0;
  366.                     if (afkDisabled)
  367.                     {
  368.                         afkDisabled = false;
  369.                         EnableUser();
  370.                     }                    
  371.                 }
  372.  
  373.                 if (lastMoveTime == 90)
  374.                 {
  375.                     afkDisabled = true;
  376.                     DisableUser();
  377.                 }                
  378.             }
  379.             public MapMarker GetMarker() => marker;
  380.            
  381.             public void ToggleMain()
  382.             {
  383.                 if (HasMapOpen())
  384.                 {                    
  385.                     ToggleMapType(lastMode);
  386.                 }
  387.                 else
  388.                 {
  389.                     lastMode = mode;
  390.                     CuiHelper.DestroyUi(player, LustyUI.Buttons);
  391.                     ToggleMapType(MapMode.Main);
  392.                 }
  393.             }
  394.             public void DisableUser()
  395.             {
  396.                 if (!mapOpen) return;
  397.                 lastMode = mode;
  398.                 CancelInvoke("UpdateMap");
  399.                 CuiHelper.DestroyUi(player, LustyUI.Buttons);
  400.                 if (mode != MapMode.None)
  401.                     LustyUI.DestroyUI(player);
  402.                 mode = MapMode.None;
  403.                 mapOpen = false;
  404.             }
  405.             public void EnableUser()
  406.             {
  407.                 if (mapOpen) return;
  408.                 ToggleMapType(lastMode);
  409.             }
  410.             public void EnterEvent() => inEvent = true;
  411.             public void ExitEvent() => inEvent = false;
  412.  
  413.             #region Friends
  414.             public bool HasFriendList(string name) => friends.ContainsKey(name);
  415.             public void AddFriendList(string name, List<string> friendlist) { friends.Add(name, friendlist); UpdateMembers(); }
  416.             public void RemoveFriendList(string name) { friends.Remove(name); UpdateMembers(); }
  417.             public void UpdateFriendList(string name, List<string> friendlist) { friends[name] = friendlist; UpdateMembers(); }
  418.  
  419.             public bool HasFriend(string name, string friendId) => friends[name].Contains(friendId);
  420.             public void AddFriend(string name, string friendId) { friends[name].Add(friendId); UpdateMembers(); }
  421.             public void RemoveFriend(string name, string friendId) { friends[name].Remove(friendId); UpdateMembers(); }
  422.             #endregion
  423.             #endregion
  424.         }
  425.  
  426.         MapUser GetUser(BasePlayer player) => player.GetComponent<MapUser>() ?? null;
  427.         MapUser GetUserByID(string playerId) => mapUsers.ContainsKey(playerId) ? mapUsers[playerId] : null;
  428.         #endregion
  429.  
  430.         #region Markers
  431.         class ActiveEntity : MonoBehaviour
  432.         {
  433.             public BaseEntity entity;
  434.             private MapMarker marker;
  435.             private AEType type;
  436.             private string icon;
  437.  
  438.             void Awake()
  439.             {
  440.                 entity = GetComponent<BaseEntity>();
  441.                 marker = new MapMarker();              
  442.             }
  443.             void OnDestroy()
  444.             {
  445.                 CancelInvoke("UpdatePosition");
  446.             }
  447.             public void SetType(AEType type)
  448.             {
  449.                 this.type = type;
  450.                 switch (type)
  451.                 {
  452.                     case AEType.None:
  453.                         break;
  454.                     case AEType.Plane:
  455.                         icon = "plane";
  456.                         marker.name = instance.msg("Plane");
  457.                         break;
  458.                     case AEType.SupplyDrop:
  459.                         icon = "supply";
  460.                         marker.name = instance.msg("Supply Drop");
  461.                         break;
  462.                     case AEType.Helicopter:
  463.                         icon = "heli";
  464.                         marker.name = instance.msg("Helicopter");
  465.                         break;
  466.                     case AEType.Debris:
  467.                         icon = "debris";
  468.                         marker.name = instance.msg("Debris");
  469.                         break;
  470.                 }
  471.                 InvokeRepeating("UpdatePosition", 0.1f, 1f);
  472.             }
  473.             public MapMarker GetMarker() => marker;
  474.             void UpdatePosition()
  475.             {                
  476.                 if (type == AEType.Helicopter || type == AEType.Plane)
  477.                 {
  478.                     marker.r = GetDirection(entity?.transform?.rotation.eulerAngles.y ?? 0);
  479.                     marker.icon = $"{icon}{marker.r}";
  480.                 }
  481.                 else marker.icon = $"{icon}";
  482.                 marker.x = GetPosition(entity.transform.position.x);
  483.                 marker.z = GetPosition(entity.transform.position.z);
  484.             }
  485.         }
  486.         class MapMarker
  487.         {
  488.             public string name { get; set; }
  489.             public float x { get; set; }
  490.             public float z { get; set; }
  491.             public float r { get; set; }
  492.             public string icon { get; set; }
  493.         }
  494.         static class MapSettings
  495.         {
  496.             static public bool minimap, complexmap, monuments, names, compass, caves, plane, heli, supply, debris, player, allplayers, friends, forcedzoom;
  497.             static public int zoomlevel;      
  498.         }
  499.         public enum MapMode
  500.         {
  501.             None,
  502.             Main,
  503.             Complex,
  504.             Minimap
  505.         }
  506.         enum AEType
  507.         {
  508.             None,
  509.             Plane,
  510.             SupplyDrop,
  511.             Helicopter,
  512.             Debris            
  513.         }
  514.         #endregion
  515.  
  516.         #region UI
  517.         class LMUI
  518.         {
  519.             static public CuiElementContainer CreateElementContainer(string panelName, string color, string aMin, string aMax, bool cursor = false, string parent = "Hud")
  520.             {
  521.                 var NewElement = new CuiElementContainer()
  522.             {
  523.                 {
  524.                     new CuiPanel
  525.                     {
  526.                         Image = {Color = color},
  527.                         RectTransform = {AnchorMin = aMin, AnchorMax = aMax},
  528.                         CursorEnabled = cursor
  529.                     },
  530.                     new CuiElement().Parent = parent,
  531.                     panelName
  532.                 }
  533.             };
  534.                 return NewElement;
  535.             }
  536.             static public void CreatePanel(ref CuiElementContainer container, string panel, string color, string aMin, string aMax, bool cursor = false)
  537.             {
  538.                 container.Add(new CuiPanel
  539.                 {
  540.                     Image = { Color = color },
  541.                     RectTransform = { AnchorMin = aMin, AnchorMax = aMax },
  542.                     CursorEnabled = cursor
  543.                 },
  544.                 panel, CuiHelper.GetGuid());
  545.             }
  546.             static public void CreateLabel(ref CuiElementContainer container, string panel, string color, string text, int size, string aMin, string aMax, TextAnchor align = TextAnchor.MiddleCenter)
  547.             {                
  548.                 container.Add(new CuiLabel
  549.                 {                    
  550.                     Text = { Color = color, FontSize = size, Align = align, FadeIn = 0, Text = text },
  551.                     RectTransform = { AnchorMin = aMin, AnchorMax = aMax }
  552.                 },
  553.                 panel, CuiHelper.GetGuid());
  554.  
  555.             }
  556.             static public void CreateButton(ref CuiElementContainer container, string panel, string color, string text, int size, string aMin, string aMax, string command, TextAnchor align = TextAnchor.MiddleCenter)
  557.             {                
  558.                 container.Add(new CuiButton
  559.                 {
  560.                     Button = { Color = color, Command = command, FadeIn = 0 },
  561.                     RectTransform = { AnchorMin = aMin, AnchorMax = aMax },
  562.                     Text = { Text = text, FontSize = size, Align = align }
  563.                 },
  564.                 panel, CuiHelper.GetGuid());
  565.             }
  566.             static public void LoadImage(ref CuiElementContainer container, string panel, string png, string aMin, string aMax)
  567.             {
  568.                 container.Add(new CuiElement
  569.                 {
  570.                     Name = CuiHelper.GetGuid(),
  571.                     Parent = panel,
  572.                     Components =
  573.                     {
  574.                         new CuiRawImageComponent {Png = png, Sprite = "assets/content/textures/generic/fulltransparent.tga" },
  575.                         new CuiRectTransformComponent {AnchorMin = aMin, AnchorMax = aMax }
  576.                     }
  577.                 });
  578.             }            
  579.         }        
  580.         #endregion
  581.  
  582.         #region Oxide Hooks
  583.         void OnNewSave(string filename)
  584.         {
  585.             isNewSave = true;
  586.         }
  587.         void Loaded()
  588.         {
  589.             imageData = Interface.Oxide.DataFileSystem.GetFile($"LustyMap{Path.DirectorySeparatorChar}ImageData");
  590.             markerData = Interface.Oxide.DataFileSystem.GetFile($"LustyMap{Path.DirectorySeparatorChar}CustomData");
  591.  
  592.             mapUsers = new Dictionary<string, MapUser>();
  593.             staticMarkers = new List<MapMarker>();
  594.             customMarkers = new Dictionary<string, MapMarker>();
  595.             temporaryMarkers = new Dictionary<uint, ActiveEntity>();
  596.             clanData = new Dictionary<string, List<string>>();
  597.  
  598.             lang.RegisterMessages(Messages, this);
  599.             permission.RegisterPermission("lustymap.admin", this);
  600.         }
  601.         void OnServerInitialized()
  602.         {
  603.             instance = this;
  604.  
  605.             worldSize = ConsoleSystem.ConVar.GetString("worldsize");
  606.             mapSeed = ConsoleSystem.ConVar.GetString("seed");
  607.             level = ConVar.Server.level;
  608.             mapSize = TerrainMeta.Size.x;
  609.  
  610.             webObject = new GameObject("WebObject");
  611.             assets = webObject.AddComponent<ImageAssets>();
  612.  
  613.             mapObject = new GameObject("MapGenObject");
  614.             mapSplitter = mapObject.AddComponent<MapSplitter>();
  615.  
  616.             LoadVariables();
  617.             LoadData();            
  618.             LoadSettings();
  619.  
  620.             FindStaticMarkers();
  621.  
  622.             ValidateImages();
  623.  
  624.             CheckFriends();
  625.             GetClans();
  626.         }
  627.         void OnPlayerInit(BasePlayer player)
  628.         {
  629.             if (player == null) return;
  630.             if (player.HasPlayerFlag(BasePlayer.PlayerFlags.ReceivingSnapshot) || player.IsSleeping())
  631.             {
  632.                 timer.In(2, () => OnPlayerInit(player));
  633.                 return;
  634.             }
  635.             if (activated)
  636.             {
  637.                 if (!string.IsNullOrEmpty(configData.MapOptions.MapKeybind))                
  638.                     player.Command("bind " + configData.MapOptions.MapKeybind + " LMUI_Control map");                              
  639.  
  640.                 var user = GetUser(player);
  641.                 if (user != null)
  642.                 {
  643.                     UnityEngine.Object.DestroyImmediate(user);
  644.                     if (mapUsers.ContainsKey(player.UserIDString))
  645.                         mapUsers.Remove(player.UserIDString);
  646.                 }
  647.  
  648.                 var mapUser = player.gameObject.AddComponent<MapUser>();
  649.                 if (!mapUsers.ContainsKey(player.UserIDString))
  650.                     mapUsers.Add(player.UserIDString, mapUser);
  651.                 mapUser.InitializeComponent();
  652.             }
  653.         }
  654.         void OnPlayerDisconnected(BasePlayer player)
  655.         {
  656.             if (player == null) return;
  657.             if (activated)
  658.             {
  659.                 if (!string.IsNullOrEmpty(configData.MapOptions.MapKeybind))
  660.                     player.Command("bind " + configData.MapOptions.MapKeybind + " \"\"");
  661.             }
  662.             if (mapUsers.ContainsKey(player.UserIDString))
  663.                 mapUsers.Remove(player.UserIDString);
  664.  
  665.             if (player.GetComponent<MapUser>())
  666.                 UnityEngine.Object.Destroy(player.GetComponent<MapUser>());
  667.  
  668.             LustyUI.DestroyUI(player);
  669.         }
  670.         void OnEntitySpawned(BaseEntity entity)
  671.         {
  672.             if (!activated) return;
  673.             if (entity == null) return;
  674.             if (entity is CargoPlane || entity is SupplyDrop || entity is BaseHelicopter || entity is HelicopterDebris)
  675.                 AddTemporaryMarker(entity);
  676.         }
  677.         void OnEntityKill(BaseNetworkable entity)
  678.         {
  679.             var activeEntity = entity.GetComponent<ActiveEntity>();
  680.             if (activeEntity == null) return;
  681.             if (entity?.net?.ID == null) return;
  682.             if (temporaryMarkers.ContainsKey(entity.net.ID))
  683.                 temporaryMarkers.Remove(entity.net.ID);
  684.             UnityEngine.Object.Destroy(activeEntity);
  685.         }
  686.         void Unload()
  687.         {
  688.             foreach (var player in BasePlayer.activePlayerList)
  689.                 OnPlayerDisconnected(player);
  690.  
  691.             var components = UnityEngine.Object.FindObjectsOfType<MapUser>();
  692.             if (components != null)
  693.                 foreach (var component in components)
  694.                     UnityEngine.Object.DestroyImmediate(component);
  695.         }
  696.         #endregion
  697.  
  698.         #region Static UI Generation
  699.         static class LustyUI
  700.         {
  701.             public static string Main = "LMUI_MapMain";
  702.             public static string Mini = "LMUI_MapMini";
  703.             public static string Complex = "LMUI_Complex";
  704.             public static string MainOverlay = "LMUI_MainOverlay";
  705.             public static string MiniOverlay = "LMUI_MiniOverlay";
  706.             public static string ComplexOverlay = "LMUI_ComplexOverlay";
  707.             public static string Buttons = "LMUI_Buttons";
  708.  
  709.             public static string MainMin;
  710.             public static string MainMax;
  711.             public static string MiniMin;
  712.             public static string MiniMax;
  713.  
  714.             public static CuiElementContainer StaticMain;
  715.             public static CuiElementContainer StaticMini;
  716.             public static Dictionary<int, CuiElementContainer[,]> StaticComplex = new Dictionary<int, CuiElementContainer[,]>();
  717.  
  718.             private static Dictionary<ulong, List<string>> OpenUI = new Dictionary<ulong, List<string>>();
  719.  
  720.             public static void RenameComponents()
  721.             {
  722.                 if (StaticMain != null)
  723.                 {
  724.                     foreach (var element in StaticMain)
  725.                     {
  726.                         if (element.Name == "AddUI CreatedPanel")
  727.                             element.Name = CuiHelper.GetGuid();
  728.                     }
  729.                 }
  730.                 if (StaticMini != null)
  731.                 {
  732.                     foreach (var element in StaticMini)
  733.                     {
  734.                         if (element.Name == "AddUI CreatedPanel")
  735.                             element.Name = CuiHelper.GetGuid();
  736.                     }
  737.                 }
  738.                 if (StaticComplex != null)
  739.                 {
  740.                     foreach (var size in StaticComplex)
  741.                     {
  742.                         foreach (var piece in size.Value)
  743.                         {
  744.                             foreach (var element in piece)
  745.                             {
  746.                                 if (element.Name == "AddUI CreatedPanel")
  747.                                     element.Name = CuiHelper.GetGuid();
  748.                             }
  749.                         }
  750.                     }
  751.                 }
  752.                 instance.activated = true;
  753.                 if (instance.configData.MapOptions.StartOpen)
  754.                     instance.ActivateMaps();
  755.             }
  756.             public static void AddBaseUI(BasePlayer player, MapMode type)
  757.             {
  758.                 var user = instance.GetUser(player);
  759.                 if (user == null) return;
  760.  
  761.                 DestroyUI(player);
  762.                 CuiElementContainer element = null;
  763.                 switch (type)
  764.                 {
  765.                     case MapMode.None:
  766.                         return;
  767.                     case MapMode.Main:
  768.                         element = StaticMain;
  769.                         CuiHelper.AddUi(player, StaticMain);
  770.                         AddElementIds(player, ref element);
  771.                         return;
  772.                     case MapMode.Complex:
  773.                         element = StaticComplex[user.Zoom()][user.Current(true), user.Current(false)];
  774.                         instance.AddMapButtons(player);
  775.                         CuiHelper.AddUi(player, element);
  776.                         AddElementIds(player, ref element);
  777.                         return;
  778.                     case MapMode.Minimap:
  779.                         element = StaticMini;
  780.                         instance.AddMapButtons(player);
  781.                         CuiHelper.AddUi(player, element);
  782.                         AddElementIds(player, ref element);
  783.                         return;                    
  784.                 }
  785.             }
  786.             private static void AddElementIds(BasePlayer player, ref CuiElementContainer container)
  787.             {
  788.                 if (!OpenUI.ContainsKey(player.userID))
  789.                     OpenUI.Add(player.userID, new List<string>());
  790.                 foreach (var piece in container)
  791.                     OpenUI[player.userID].Add(piece.Name);              
  792.             }
  793.             public static void DestroyUI(BasePlayer player)
  794.             {
  795.                 CuiHelper.DestroyUi(player, Buttons);
  796.                 CuiHelper.DestroyUi(player, Main);
  797.                 CuiHelper.DestroyUi(player, MainOverlay);
  798.                 CuiHelper.DestroyUi(player, Mini);
  799.                 CuiHelper.DestroyUi(player, MiniOverlay);
  800.                 CuiHelper.DestroyUi(player, Complex);
  801.                 CuiHelper.DestroyUi(player, ComplexOverlay);
  802.                 if (!OpenUI.ContainsKey(player.userID)) return;
  803.                 foreach (var piece in OpenUI[player.userID])
  804.                     CuiHelper.DestroyUi(player, piece);
  805.             }
  806.             /* public static void DestroyUI(BasePlayer player, MapMode type)
  807.              {
  808.                  CuiHelper.DestroyUi(player, Buttons);
  809.                  CuiElementContainer element = null;
  810.  
  811.                  switch (type)
  812.                  {
  813.                      case MapMode.Main:
  814.                          element = StaticMain;
  815.                          CuiHelper.DestroyUi(player, Main);
  816.                          CuiHelper.DestroyUi(player, MainOverlay);
  817.                          break;
  818.                      case MapMode.Minimap:
  819.                          element = StaticMini;
  820.                          CuiHelper.DestroyUi(player, Mini);
  821.                          CuiHelper.DestroyUi(player, MiniOverlay);
  822.                          break;
  823.                      case MapMode.Complex:
  824.                          {
  825.                              var user = instance.GetUser(player);
  826.                              if (user != null)
  827.                              {
  828.                                  int index = user.Zoom();
  829.                                  int row = user.Position(false);
  830.                                  int column = user.Position(true);
  831.                                  foreach (var e in StaticComplex[index][column, row])
  832.                                      CuiHelper.DestroyUi(player, e.Name);
  833.                              }
  834.                              CuiHelper.DestroyUi(player, Complex);
  835.                              CuiHelper.DestroyUi(player, ComplexOverlay);
  836.                          }
  837.                          return;
  838.                      case MapMode.None:
  839.                          return;
  840.                  }
  841.  
  842.                  if (element != null)
  843.                      foreach (var piece in element)
  844.                          CuiHelper.DestroyUi(player, piece.Name);
  845.              }    
  846.              public static void DestroyAllUI(BasePlayer player) // Avoid if possible
  847.              {
  848.                  if (player == null) return;
  849.  
  850.                  if (StaticMain != null)
  851.                      foreach (var piece in StaticMain)
  852.                          CuiHelper.DestroyUi(player, piece.Name);
  853.  
  854.                  if (StaticMini != null)
  855.                      foreach (var piece in StaticMini)
  856.                          CuiHelper.DestroyUi(player, piece.Name);
  857.  
  858.                  if (StaticComplex != null)
  859.                      foreach (var piece in StaticComplex)
  860.                          foreach(var e in piece.Value)
  861.                              foreach (var c in e)
  862.                                  CuiHelper.DestroyUi(player, c.Name);
  863.  
  864.                  CuiHelper.DestroyUi(player, Buttons);
  865.                  CuiHelper.DestroyUi(player, Main);
  866.                  CuiHelper.DestroyUi(player, MainOverlay);              
  867.                  CuiHelper.DestroyUi(player, Mini);
  868.                  CuiHelper.DestroyUi(player, MiniOverlay);                
  869.                  CuiHelper.DestroyUi(player, Complex);
  870.                  CuiHelper.DestroyUi(player, ComplexOverlay);
  871.              }            */
  872.             public static string Color(string hexColor, float alpha)
  873.             {
  874.                 int red = int.Parse(hexColor.Substring(0, 2), NumberStyles.AllowHexSpecifier);
  875.                 int green = int.Parse(hexColor.Substring(2, 2), NumberStyles.AllowHexSpecifier);
  876.                 int blue = int.Parse(hexColor.Substring(4, 2), NumberStyles.AllowHexSpecifier);
  877.                 return $"{(double)red / 255} {(double)green / 255} {(double)blue / 255} {alpha}";
  878.             }
  879.         }
  880.  
  881.         void GenerateMaps(bool main, bool mini, bool complex)
  882.         {
  883.             if (main) CreateStaticMain();
  884.             SetMinimapSize();
  885.             if (mini) CreateStaticMini();
  886.             if (complex) CreateStaticComplex();
  887.         }
  888.         void SetMinimapSize()
  889.         {
  890.             float startx = 0f + configData.MapOptions.MinimapOptions.OffsetSide;
  891.             float endx = startx + (0.13f * configData.MapOptions.MinimapOptions.HorizontalScale);
  892.             float endy = 1f - configData.MapOptions.MinimapOptions.OffsetTop;
  893.             float starty = endy - (0.2301f * configData.MapOptions.MinimapOptions.VerticalScale);
  894.             if (!configData.MapOptions.MinimapOptions.OnLeftSide)
  895.             {
  896.                 endx = 1 - configData.MapOptions.MinimapOptions.OffsetSide;
  897.                 startx = endx - (0.13f * configData.MapOptions.MinimapOptions.HorizontalScale);
  898.             }
  899.             LustyUI.MiniMin = $"{startx} {starty}";
  900.             LustyUI.MiniMax = $"{endx} {endy}";
  901.         }
  902.         void CreateStaticMain()
  903.         {
  904.             PrintWarning("Generating the main map");
  905.             var mapimage = GetImage("mapimage");
  906.             if (string.IsNullOrEmpty(mapimage))
  907.             {
  908.                 PrintError("Unable to load the map image from file storage. This may be caused by slow processing of the images being uploaded to your server. If this problem persists unload the plugin and delete your ImageData.json data file");
  909.                 activated = false;
  910.                 return;
  911.             }
  912.             float iconsize = 0.01f;
  913.             LustyUI.MainMin = "0.2271875 0.015";
  914.             LustyUI.MainMax = "0.7728125 0.985";
  915.  
  916.             var mapContainer = LMUI.CreateElementContainer(LustyUI.Main, "0 0 0 1", LustyUI.MainMin, LustyUI.MainMax, string.IsNullOrEmpty(configData.MapOptions.MapKeybind));
  917.             LMUI.LoadImage(ref mapContainer, LustyUI.Main, mapimage, "0 0", "1 1");
  918.             LMUI.CreatePanel(ref mapContainer, LustyUI.Main, LustyUI.Color("2b627a", 0.4f), "0 0.96", "1 1");
  919.             LMUI.CreateLabel(ref mapContainer, LustyUI.Main, "", $"{Title}  v{Version}", 14, "0.01 0.96", "0.99 1");            
  920.  
  921.             foreach(var marker in staticMarkers)
  922.             {
  923.                 var image = GetImage(marker.icon);
  924.                 if (string.IsNullOrEmpty(image)) continue;
  925.                 LMUI.LoadImage(ref mapContainer, LustyUI.Main, image, $"{marker.x - iconsize} {marker.z - iconsize}", $"{marker.x + iconsize} {marker.z + iconsize}");
  926.                 if (MapSettings.names)
  927.                     LMUI.CreateLabel(ref mapContainer, LustyUI.Main, "", marker.name, 10, $"{marker.x - 0.1} {marker.z - iconsize - 0.03}", $"{marker.x + 0.1} {marker.z - iconsize}");
  928.             }
  929.             LustyUI.StaticMain = mapContainer;
  930.             PrintWarning("Main map generated successfully!");
  931.             if (!MapSettings.minimap)            
  932.                 LustyUI.RenameComponents();
  933.         }
  934.         void CreateStaticMini()
  935.         {
  936.             PrintWarning("Generating the mini-map");
  937.             var mapimage = GetImage("mapimage");
  938.             if (string.IsNullOrEmpty(mapimage))
  939.             {
  940.                 PrintError("Unable to load the map image from file storage. This may be caused by slow processing of the images being uploaded to your server. If this problem persists unload the plugin and delete your ImageData.json data file");
  941.                 activated = false;
  942.                 return;
  943.             }
  944.             float iconsize = 0.03f;
  945.            
  946.             var mapContainer = LMUI.CreateElementContainer(LustyUI.Mini, "0 0 0 1", LustyUI.MiniMin, LustyUI.MiniMax);
  947.             LMUI.LoadImage(ref mapContainer, LustyUI.Mini, mapimage, "0 0", "1 1");
  948.  
  949.             foreach (var marker in staticMarkers)
  950.             {
  951.                 var image = GetImage(marker.icon);
  952.                 if (string.IsNullOrEmpty(image)) continue;
  953.                 LMUI.LoadImage(ref mapContainer, LustyUI.Mini, image, $"{marker.x - iconsize} {marker.z - iconsize}", $"{marker.x + iconsize} {marker.z + iconsize}");                
  954.             }
  955.             LustyUI.StaticMini = mapContainer;
  956.             PrintWarning("Mini map generated successfully!");
  957.             if (!MapSettings.complexmap)            
  958.                 LustyUI.RenameComponents();            
  959.         }      
  960.         void CreateStaticComplex()
  961.         {
  962.             PrintWarning("Generating the complex map. This may take a few moments, please wait!");            
  963.             foreach (var mapslices in new List<int> { 6, 12, 26 })//, 32 })
  964.             {
  965.                 for (int number = 0; number < (mapslices * mapslices); number++)
  966.                 {
  967.                     int rowNum = 0;
  968.                     int colNum = 0;
  969.                     if (number > mapslices - 1)
  970.                     {
  971.                         colNum = Convert.ToInt32(Math.Floor((float)number / (float)mapslices));
  972.                         rowNum = number - (colNum * mapslices);
  973.                     }
  974.                     else rowNum = number;
  975.                    
  976.                     var mapContainer = LMUI.CreateElementContainer(LustyUI.Complex, "0 0 0 1", LustyUI.MiniMin, LustyUI.MiniMax);
  977.  
  978.                     string imageId = GetImage($"map-{mapslices}-{rowNum}-{colNum}");
  979.                     if (!string.IsNullOrEmpty(imageId))
  980.                         LMUI.LoadImage(ref mapContainer, LustyUI.Complex, imageId, $"0 0", $"1 1");
  981.                     else
  982.                     {
  983.                         PrintError($"Missing map piece (Slices: {mapslices}, Column: {colNum}, Row: {rowNum}). When the plugin is splitting the map you must wait for it to finish otherwise the split will not complete and this error will occur");
  984.                         PrintError($"The plugin will now clear all image data and reload itself to re-initialize the map splitter.");
  985.                         storedImages.data.Clear();
  986.                         SaveData();
  987.                         rust.RunServerCommand("oxide.reload", new object[] { "LustyMap" });
  988.                         return;
  989.                     }
  990.  
  991.                     double width = ((double)1 / (double)mapslices);
  992.                     float iconsize = 0.03f;
  993.  
  994.                     var column = colNum;
  995.                     var row = rowNum;
  996.                     if (column < 1) column = 1;
  997.                     if (column > mapslices - 2) column = mapslices - 2;
  998.                     if (row < 1) row = 1;
  999.                     if (row > mapslices - 2) row = mapslices - 2;                    
  1000.  
  1001.                     double colStart = (width * column) - width;
  1002.                     double colEnd = colStart + (width * 3);
  1003.  
  1004.                     double rowStart = 1 - ((width * row) - width);
  1005.                     double rowEnd = (rowStart - (width * 3));                  
  1006.  
  1007.                     foreach (var marker in staticMarkers)
  1008.                     {
  1009.                         string markerId = GetImage(marker.icon);
  1010.                         if (string.IsNullOrEmpty(markerId)) continue;
  1011.  
  1012.                         float x = marker.x;
  1013.                         float z = marker.z;
  1014.                         if ((x > colStart && x < colEnd) && (z > rowEnd && z < rowStart))
  1015.                         {
  1016.                             var average = 1 / (colEnd - colStart);
  1017.                             double posX = (x - colStart) * average;
  1018.                             double posZ = (z - rowEnd) * average;
  1019.                             LMUI.LoadImage(ref mapContainer, LustyUI.Complex, markerId, $"{posX - iconsize} {posZ - iconsize}", $"{posX + iconsize} {posZ + iconsize}");
  1020.                         }
  1021.                     }
  1022.                     int zoom = CountToZoom(mapslices);
  1023.  
  1024.                     if (!LustyUI.StaticComplex.ContainsKey(zoom))
  1025.                         LustyUI.StaticComplex.Add(zoom, new CuiElementContainer[mapslices, mapslices]);
  1026.                     LustyUI.StaticComplex[zoom][colNum, rowNum] = mapContainer;
  1027.                 }
  1028.             }
  1029.             PrintWarning("Complex map generated successfully!");
  1030.             LustyUI.RenameComponents();            
  1031.         }
  1032.        
  1033.         static int ZoomToCount(int zoom)
  1034.         {
  1035.             switch (zoom)
  1036.             {
  1037.                 case 1:
  1038.                     return 6;
  1039.                 case 2:
  1040.                     return 12;
  1041.                 case 3:
  1042.                     return 26;
  1043.                 case 4:
  1044.                     return 32;
  1045.                 default:
  1046.                     return 0;
  1047.             }
  1048.         }
  1049.         static int CountToZoom(int count)
  1050.         {
  1051.             switch (count)
  1052.             {
  1053.                 case 6:
  1054.                     return 1;
  1055.                 case 12:
  1056.                     return 2;
  1057.                 case 26:
  1058.                     return 3;
  1059.                 case 32:
  1060.                     return 4;
  1061.                 default:
  1062.                     return 0;
  1063.             }
  1064.         }
  1065.         #endregion
  1066.  
  1067.         #region Maps
  1068.         void ActivateMaps()
  1069.         {
  1070.             foreach (var player in BasePlayer.activePlayerList)            
  1071.                 OnPlayerInit(player);            
  1072.         }
  1073.         void AddMapCompass(BasePlayer player, ref CuiElementContainer mapContainer, string panel, int fontsize, string offsetMin, string offsetMax)
  1074.         {
  1075.             string direction = null;
  1076.             if (player?.eyes?.rotation == null) return;
  1077.             float lookRotation = player?.eyes?.rotation.eulerAngles.y ?? 0;
  1078.             int playerdirection = (Convert.ToInt16((lookRotation - 5) / 10 + 0.5) * 10);
  1079.             if (lookRotation >= 355) playerdirection = 0;
  1080.             if (lookRotation > 337.5 || lookRotation < 22.5) { direction = msg("cpsN"); }
  1081.             else if (lookRotation > 22.5 && lookRotation < 67.5) { direction = msg("cpsNE"); }
  1082.             else if (lookRotation > 67.5 && lookRotation < 112.5) { direction = msg("cpsE"); }
  1083.             else if (lookRotation > 112.5 && lookRotation < 157.5) { direction = msg("cpsSE"); }
  1084.             else if (lookRotation > 157.5 && lookRotation < 202.5) { direction = msg("cpsS"); }
  1085.             else if (lookRotation > 202.5 && lookRotation < 247.5) { direction = msg("cpsSW"); }
  1086.             else if (lookRotation > 247.5 && lookRotation < 292.5) { direction = msg("cpsW"); }
  1087.             else if (lookRotation > 292.5 && lookRotation < 337.5) { direction = msg("cpsNW"); }
  1088.             LMUI.CreateLabel(ref mapContainer, panel, "", $"<size={fontsize + 4}>{direction}</size> \n{player.transform.position}", fontsize, offsetMin, offsetMax, TextAnchor.UpperCenter);
  1089.         }
  1090.         void AddMapButtons(BasePlayer player)
  1091.         {
  1092.             float startx = 0f + configData.MapOptions.MinimapOptions.OffsetSide;
  1093.             float endx = startx + (0.13f * configData.MapOptions.MinimapOptions.HorizontalScale);
  1094.             float endy = 1f - configData.MapOptions.MinimapOptions.OffsetTop;
  1095.             float starty = endy - (0.2301f * configData.MapOptions.MinimapOptions.VerticalScale);
  1096.             string b_text = "<<<";
  1097.             var container = LMUI.CreateElementContainer(LustyUI.Buttons, "0 0 0 0", $"{endx + 0.001f} {starty}", $"{endx + 0.02f} {endy}");
  1098.  
  1099.             if (!configData.MapOptions.MinimapOptions.OnLeftSide)
  1100.             {
  1101.                 endx = 1 - configData.MapOptions.MinimapOptions.OffsetSide;
  1102.                 startx = endx - (0.13f * configData.MapOptions.MinimapOptions.HorizontalScale);
  1103.                 b_text = ">>>";
  1104.                 container = LMUI.CreateElementContainer(LustyUI.Buttons, "0 0 0 0", $"{startx - 0.02f} {starty}", $"{startx - 0.001f} {endy}");
  1105.             }
  1106.            
  1107.             LMUI.CreateButton(ref container, LustyUI.Buttons, LustyUI.Color("696969", 0.6f), b_text, 12, $"0 0.9", $"1 1", "LMUI_Control shrink");
  1108.             if (MapSettings.complexmap && !MapSettings.forcedzoom)
  1109.             {
  1110.                 LMUI.CreateButton(ref container, LustyUI.Buttons, LustyUI.Color("696969", 0.6f), "+", 14, $"0 0.79", $"1 0.89", "LMUI_Control zoomin");
  1111.                 LMUI.CreateButton(ref container, LustyUI.Buttons, LustyUI.Color("696969", 0.6f), "-", 14, $"0 0.68", $"1 0.78", "LMUI_Control zoomout");
  1112.             }
  1113.             CuiHelper.DestroyUi(player, LustyUI.Buttons);
  1114.             CuiHelper.AddUi(player, container);
  1115.         }
  1116.         void CreateShrunkUI(BasePlayer player)
  1117.         {
  1118.             var user = GetUser(player);
  1119.             if (user == null) return;
  1120.  
  1121.             float b_endy = 0.999f - configData.MapOptions.MinimapOptions.OffsetTop;
  1122.             float b_startx = 0.001f + configData.MapOptions.MinimapOptions.OffsetSide;
  1123.             float b_endx = b_startx + 0.02f;
  1124.             string b_text = ">>>";
  1125.  
  1126.             if (!configData.MapOptions.MinimapOptions.OnLeftSide)
  1127.             {                
  1128.                 b_endx = 0.999f - configData.MapOptions.MinimapOptions.OffsetSide;
  1129.                 b_startx = b_endx - 0.02f;
  1130.                 b_text = "<<<";
  1131.             }                      
  1132.             var container = LMUI.CreateElementContainer(LustyUI.Buttons, "0 0 0 0", $"{b_startx} {b_endy - 0.025f}", $"{b_endx} {b_endy}");
  1133.             LMUI.CreateButton(ref container, LustyUI.Buttons, LustyUI.Color("696969", 0.6f), b_text, 12, "0 0", "1 1", "LMUI_Control expand");
  1134.             CuiHelper.DestroyUi(player, LustyUI.Buttons);
  1135.             CuiHelper.AddUi(player, container);
  1136.         }
  1137.  
  1138.         #region Standard Maps
  1139.         void OpenMainMap(BasePlayer player) => LustyUI.AddBaseUI(player, MapMode.Main);
  1140.         void OpenMiniMap(BasePlayer player) => LustyUI.AddBaseUI(player, MapMode.Minimap);
  1141.         void UpdateOverlay(BasePlayer player, string panel, string posMin, string posMax, float iconsize)
  1142.         {
  1143.             var mapContainer = LMUI.CreateElementContainer(panel, "0 0 0 0", posMin, posMax);
  1144.  
  1145.             var user = GetUser(player);
  1146.             if (user == null) return;            
  1147.             foreach (var marker in customMarkers)
  1148.             {
  1149.                 var image = GetImage(marker.Value.icon);
  1150.                 if (string.IsNullOrEmpty(image)) continue;
  1151.                 AddIconToMap(ref mapContainer, panel, image, marker.Value.name, iconsize * 1.25f, marker.Value.x, marker.Value.z);
  1152.             }
  1153.             foreach (var entity in temporaryMarkers)
  1154.             {
  1155.                 var marker = entity.Value.GetMarker();
  1156.                 if (marker == null) continue;
  1157.                 var image = GetImage(marker.icon);
  1158.                 if (string.IsNullOrEmpty(image)) continue;
  1159.                 AddIconToMap(ref mapContainer, panel, image, "", iconsize * 1.4f, marker.x, marker.z);
  1160.             }            
  1161.             if (user.IsAdmin() || MapSettings.allplayers)
  1162.             {
  1163.                 foreach (var mapuser in mapUsers)
  1164.                 {
  1165.                     if (mapuser.Key == player.UserIDString) continue;
  1166.  
  1167.                     var marker = mapuser.Value.GetMarker();
  1168.                     if (marker == null) continue;
  1169.                     var image = GetImage($"other{marker.r}");
  1170.                     if (string.IsNullOrEmpty(image)) continue;
  1171.                     AddIconToMap(ref mapContainer, panel, image, marker.name, iconsize * 1.25f, marker.x, marker.z);                    
  1172.                 }
  1173.             }
  1174.             else if (MapSettings.friends)
  1175.             {
  1176.                 foreach (var friendId in user.friendList)
  1177.                 {
  1178.                     if (friendId == player.UserIDString) continue;
  1179.  
  1180.                     if (mapUsers.ContainsKey(friendId))
  1181.                     {
  1182.                         var friend = mapUsers[friendId];
  1183.                         if (friend.InEvent() && configData.MapOptions.HideEventPlayers) continue;
  1184.                         var marker = friend.GetMarker();
  1185.                         if (marker == null) continue;
  1186.                         var image = GetImage($"friend{marker.r}");
  1187.                         if (string.IsNullOrEmpty(image)) continue;
  1188.                         AddIconToMap(ref mapContainer, panel, image, marker.name, iconsize * 1.25f, marker.x, marker.z);
  1189.                     }
  1190.                 }
  1191.             }
  1192.             if (MapSettings.player)
  1193.             {
  1194.                 var selfMarker = user.GetMarker();
  1195.                 if (selfMarker != null)
  1196.                 {
  1197.                     var selfImage = GetImage($"self{selfMarker.r}");
  1198.                     AddIconToMap(ref mapContainer, panel, selfImage, "", iconsize * 1.25f, selfMarker.x, selfMarker.z);
  1199.                 }
  1200.             }
  1201.  
  1202.             if (panel == LustyUI.MainOverlay)
  1203.             {
  1204.                 if (string.IsNullOrEmpty(configData.MapOptions.MapKeybind))
  1205.                     LMUI.CreateButton(ref mapContainer, panel, LustyUI.Color("88a8b6", 1), "X", 14, "0.95 0.961", "0.999 0.999", "LMUI_Control closeui");
  1206.                 if (MapSettings.compass)
  1207.                     AddMapCompass(player, ref mapContainer, panel, 14, "0.75 0.88", "1 0.95");
  1208.             }
  1209.  
  1210.             if (panel == LustyUI.MiniOverlay)
  1211.             {                
  1212.                 if (MapSettings.compass)
  1213.                     AddMapCompass(player, ref mapContainer, panel, 10, "0 -0.25", "1 -0.02");                
  1214.             }
  1215.  
  1216.             CuiHelper.DestroyUi(player, panel);
  1217.             CuiHelper.AddUi(player, mapContainer);
  1218.         }
  1219.         void AddIconToMap(ref CuiElementContainer mapContainer, string panel, string image, string name, float iconsize, float posX, float posZ)
  1220.         {
  1221.             if (posX < iconsize || posX > 1 - iconsize || posZ < iconsize || posZ > 1 - iconsize) return;
  1222.             LMUI.LoadImage(ref mapContainer, panel, image, $"{posX - iconsize} {posZ - iconsize}", $"{posX + iconsize} {posZ + iconsize}");
  1223.             if (MapSettings.names)
  1224.                 LMUI.CreateLabel(ref mapContainer, panel, "", name, 10, $"{posX - 0.1} {posZ - iconsize - 0.025}", $"{posX + 0.1} {posZ - iconsize}");
  1225.         }        
  1226.         #endregion
  1227.  
  1228.         #region Complex Maps
  1229.         void OpenComplexMap(BasePlayer player) => LustyUI.AddBaseUI(player, MapMode.Complex);
  1230.         void UpdateCompOverlay(BasePlayer player)
  1231.         {
  1232.             var mapContainer = LMUI.CreateElementContainer(LustyUI.ComplexOverlay, "0 0 0 0", LustyUI.MiniMin, LustyUI.MiniMax);
  1233.  
  1234.             var user = GetUser(player);
  1235.             if (user == null) return;
  1236.  
  1237.             var colNum = user.Current(true);
  1238.             var rowNum = user.Current(false);
  1239.  
  1240.             var mapslices = ZoomToCount(user.Zoom());
  1241.             double width = ((double)1 / (double)mapslices);
  1242.             float iconsize = 0.04f;
  1243.  
  1244.             var column = colNum;
  1245.             var row = rowNum;
  1246.             if (column < 1) column = 1;
  1247.             if (column > mapslices - 2) column = mapslices - 2;
  1248.             if (row < 1) row = 1;
  1249.             if (row > mapslices - 2) row = mapslices - 2;
  1250.  
  1251.             double colStart = (width * column) - width;
  1252.             double colEnd = colStart + (width * 3);
  1253.  
  1254.             double rowStart = 1 - ((width * row) - width);
  1255.             double rowEnd = (rowStart - (width * 3));
  1256.  
  1257.             foreach (var marker in customMarkers)
  1258.             {
  1259.                 var image = GetImage(marker.Value.icon);
  1260.                 if (string.IsNullOrEmpty(image)) continue;
  1261.                 AddComplexIcon(ref mapContainer, LustyUI.ComplexOverlay, image, "", iconsize * 1.3f, marker.Value.x, marker.Value.z, colStart, colEnd, rowStart, rowEnd);
  1262.             }
  1263.             foreach (var entity in temporaryMarkers)
  1264.             {
  1265.                 var marker = entity.Value.GetMarker();
  1266.                 if (marker == null) continue;
  1267.                 var image = GetImage(marker.icon);
  1268.                 if (string.IsNullOrEmpty(image)) continue;
  1269.                 AddComplexIcon(ref mapContainer, LustyUI.ComplexOverlay, image, "", iconsize * 1.6f, marker.x, marker.z, colStart, colEnd, rowStart, rowEnd);
  1270.             }
  1271.             if (user.IsAdmin() || MapSettings.allplayers)
  1272.             {
  1273.                 foreach (var mapuser in mapUsers)
  1274.                 {
  1275.                     if (mapuser.Key == player.UserIDString) continue;
  1276.  
  1277.                     var marker = mapuser.Value.GetMarker();
  1278.                     if (marker == null) continue;
  1279.                     var image = GetImage($"other{marker.r}");
  1280.                     if (string.IsNullOrEmpty(image)) continue;
  1281.                     AddComplexIcon(ref mapContainer, LustyUI.ComplexOverlay, image, "", iconsize * 1.3f, marker.x, marker.z, colStart, colEnd, rowStart, rowEnd);
  1282.                 }
  1283.             }
  1284.             else if (MapSettings.friends)
  1285.             {
  1286.                 foreach (var friendId in user.friendList)
  1287.                 {
  1288.                     if (friendId == player.UserIDString) continue;
  1289.  
  1290.                     if (mapUsers.ContainsKey(friendId))
  1291.                     {
  1292.                         var friend = mapUsers[friendId];
  1293.                         if (friend.InEvent() && configData.MapOptions.HideEventPlayers) continue;
  1294.                         var marker = friend.GetMarker();
  1295.                         if (marker == null) continue;
  1296.                         var image = GetImage($"friend{marker.r}");
  1297.                         if (string.IsNullOrEmpty(image)) continue;
  1298.                         AddComplexIcon(ref mapContainer, LustyUI.ComplexOverlay, image, "", iconsize * 1.3f, marker.x, marker.z, colStart, colEnd, rowStart, rowEnd);
  1299.                     }
  1300.                 }
  1301.             }            
  1302.             if (MapSettings.player)
  1303.             {
  1304.                 var selfMarker = user.GetMarker();
  1305.                 if (selfMarker != null)
  1306.                 {
  1307.                     var selfImage = GetImage($"self{selfMarker.r}");
  1308.                     if (!string.IsNullOrEmpty(selfImage))
  1309.                         AddComplexIcon(ref mapContainer, LustyUI.ComplexOverlay, selfImage, "", iconsize * 1.25f, selfMarker.x, selfMarker.z, colStart, colEnd, rowStart, rowEnd);
  1310.                 }
  1311.             }
  1312.             if (MapSettings.compass)
  1313.                 AddMapCompass(player, ref mapContainer, LustyUI.ComplexOverlay, 10, "0 -0.25", "1 -0.02");
  1314.            
  1315.             CuiHelper.DestroyUi(player, LustyUI.ComplexOverlay);
  1316.             CuiHelper.AddUi(player, mapContainer);
  1317.         }
  1318.         void AddComplexIcon(ref CuiElementContainer mapContainer, string panel, string image, string name, float iconsize, float x, float z, double colStart, double colEnd, double rowStart, double rowEnd)
  1319.         {
  1320.             if ((x > colStart && x < colEnd) && (z > rowEnd && z < rowStart))
  1321.             {
  1322.                 var average = 1 / (colEnd - colStart);
  1323.                 double posX = (x - colStart) * average;
  1324.                 double posZ = (z - rowEnd) * average;
  1325.  
  1326.                 if (posX < 0 + iconsize || posX > 1 - iconsize || posZ < 0 + iconsize || posZ > 1 - iconsize) return;
  1327.                 LMUI.LoadImage(ref mapContainer, panel, image, $"{posX - iconsize} {posZ - iconsize}", $"{posX + iconsize} {posZ + iconsize}");
  1328.                 if (MapSettings.names)
  1329.                     LMUI.CreateLabel(ref mapContainer, panel, "", name, 10, $"{posX - 0.1} {posZ - iconsize - 0.025}", $"{posX + 0.1} {posZ - iconsize}");
  1330.             }
  1331.         }
  1332.         #endregion
  1333.         #endregion
  1334.  
  1335.         #region Commands
  1336.         [ConsoleCommand("LMUI_Control")]
  1337.         private void cmdLustyControl(ConsoleSystem.Arg arg)
  1338.         {
  1339.             if (!activated) return;
  1340.             var player = arg.connection.player as BasePlayer;
  1341.             if (player == null)
  1342.                 return;
  1343.             var user = GetUser(player);
  1344.             if (user == null) return;
  1345.             switch (arg.Args[0].ToLower())
  1346.             {
  1347.                 case "map":                    
  1348.                     user.ToggleMain();
  1349.                     return;
  1350.                 case "closeui":
  1351.                     if (MapSettings.minimap)
  1352.                     {
  1353.                         if (user.Zoom() > 0)
  1354.                             user.ToggleMapType(MapMode.Complex);
  1355.                         else user.ToggleMapType(MapMode.Minimap);
  1356.                     }
  1357.                     else user.ToggleMapType(MapMode.None);
  1358.                     return;
  1359.                 case "shrink":
  1360.                     user.ToggleMapType(MapMode.None);
  1361.                     return;
  1362.                 case "expand":
  1363.                     if (user.Zoom() > 0)
  1364.                         user.ToggleMapType(MapMode.Complex);
  1365.                     else user.ToggleMapType(MapMode.Minimap);
  1366.                     return;
  1367.                 case "zoomin":
  1368.                     user.Zoom(true);
  1369.                     break;
  1370.                 case "zoomout":
  1371.                     user.Zoom(false);
  1372.                     return;
  1373.                 default:
  1374.                     return;
  1375.             }
  1376.         }
  1377.         [ConsoleCommand("resetmap")]
  1378.         void ccmdResetmap(ConsoleSystem.Arg arg)
  1379.         {
  1380.             if (arg.connection != null) return;
  1381.             SendReply(arg, "ResetMap Confirmed! Removing stored images and reloading the plugin to re-download your map image");
  1382.             storedImages.data.Clear();
  1383.             SaveData();
  1384.             rust.RunServerCommand("oxide.reload", new object[] { "LustyMap " });
  1385.         }
  1386.         [ChatCommand("map")]
  1387.         void cmdOpenMap(BasePlayer player, string command, string[] args)
  1388.         {
  1389.             if (!activated)
  1390.             {
  1391.                 SendReply(player, "LustyMap is not activated");
  1392.                 return;
  1393.             }
  1394.             var user = GetUser(player);
  1395.             if (user == null)
  1396.             {
  1397.                 user = player.gameObject.AddComponent<MapUser>();
  1398.                 mapUsers.Add(player.UserIDString, user);
  1399.                 user.InitializeComponent();
  1400.             }
  1401.             if (args.Length == 0)
  1402.                 user.ToggleMapType(MapMode.Main);
  1403.             else
  1404.             {
  1405.                 if (args[0].ToLower() == "mini")
  1406.                     user.ToggleMapType(MapMode.Minimap);
  1407.                 if (args[0].ToLower() == "admin")
  1408.                 {
  1409.                     if (!player.IsAdmin() && !permission.UserHasPermission(player.UserIDString, "lustymap.admin")) return;
  1410.                     if (user.IsAdmin())
  1411.                     {
  1412.                         user.ToggleAdmin(false);
  1413.                         SendReply(player, "Admin mode disabled");
  1414.                     }
  1415.                     else
  1416.                     {
  1417.                         user.ToggleAdmin(true);
  1418.                         SendReply(player, "Admin mode enabled");
  1419.                     }
  1420.                 }
  1421.             }
  1422.         }
  1423.  
  1424.         [ChatCommand("marker")]
  1425.         void cmdMarker(BasePlayer player, string command, string[] args)
  1426.         {
  1427.             if (!player.IsAdmin() && !permission.UserHasPermission(player.UserIDString, "lustymap.admin")) return;
  1428.             if (args.Length == 0)
  1429.             {
  1430.                 SendReply(player, "Add and remove markers / custom icons to the map. See the overview for information regarding using custom icons");
  1431.                 SendReply(player, "/marker add <name> <opt:iconname> - Adds a new markers with the name specified at your location.");
  1432.                 SendReply(player, "/marker remove <name> - Removes the marker with the name specified");
  1433.                 return;
  1434.             }
  1435.             if (args.Length < 2)
  1436.             {
  1437.                 SendReply(player, "You must enter a marker name!");
  1438.                 return;
  1439.             }
  1440.             var name = args[1];
  1441.             switch (args[0].ToLower())
  1442.             {
  1443.                 case "add":
  1444.                     string icon = "special";
  1445.                     if (args.Length > 2)
  1446.                         icon = args[2];
  1447.                     if (AddMarker(player.transform.position.x, player.transform.position.z, name, icon))
  1448.                         SendReply(player, $"You have successfully added a new map marker with the name: {name}");  
  1449.                     else SendReply(player, $"A map marker with the name \"{name}\" already exists");
  1450.                     return;
  1451.                 case "remove":
  1452.                     if (RemoveMarker(name))
  1453.                         SendReply(player, $"You have successfully removed the map marker with the name: {name}");
  1454.                     else SendReply(player, $"No map marker with the name \"{name}\" exists");
  1455.                     return;
  1456.                 default:
  1457.                     SendReply(player, "Incorrect syntax used. type \"/marker\" for more information");
  1458.                     break;
  1459.             }
  1460.         }
  1461.         #endregion
  1462.  
  1463.         #region Functions    
  1464.         private void CheckFriends()
  1465.         {
  1466.             if (Friends)
  1467.             {
  1468.                 if (Friends.ResourceId == 686)              
  1469.                     isRustFriends = true;
  1470.             }
  1471.         }
  1472.         private void AddTemporaryMarker(BaseEntity entity)
  1473.         {
  1474.             if (entity == null) return;
  1475.             if (entity?.net?.ID == null) return;
  1476.             AEType type = AEType.None;
  1477.             if (entity is CargoPlane)
  1478.             {
  1479.                 if (!configData.MapMarkers.ShowPlanes) return;
  1480.                 type = AEType.Plane;                
  1481.             }
  1482.             else if (entity is BaseHelicopter)
  1483.             {
  1484.                 if (!configData.MapMarkers.ShowHelicopters) return;
  1485.                 type = AEType.Helicopter;              
  1486.             }
  1487.             else if (entity is SupplyDrop)
  1488.             {
  1489.                 if (!configData.MapMarkers.ShowSupplyDrops) return;
  1490.                 type = AEType.SupplyDrop;                
  1491.             }
  1492.             else if (entity is HelicopterDebris)
  1493.             {
  1494.                 if (!configData.MapMarkers.ShowDebris) return;
  1495.                 type = AEType.Debris;                
  1496.             }          
  1497.             var actEnt = entity.gameObject.AddComponent<ActiveEntity>();
  1498.             actEnt.SetType(type);
  1499.  
  1500.             temporaryMarkers.Add(entity.net.ID, actEnt);
  1501.         }
  1502.         private void LoadSettings()
  1503.         {
  1504.             MapSettings.caves = configData.MapMarkers.ShowCaves;
  1505.             MapSettings.compass = configData.MapOptions.ShowCompass;
  1506.             MapSettings.debris = configData.MapMarkers.ShowDebris;
  1507.             MapSettings.heli = configData.MapMarkers.ShowHelicopters;
  1508.             MapSettings.monuments = configData.MapMarkers.ShowMonuments;
  1509.             MapSettings.plane = configData.MapMarkers.ShowPlanes;
  1510.             MapSettings.player = configData.MapMarkers.ShowPlayer;
  1511.             MapSettings.allplayers = configData.MapMarkers.ShowAllPlayers;
  1512.             MapSettings.supply = configData.MapMarkers.ShowSupplyDrops;
  1513.             MapSettings.friends = configData.MapMarkers.ShowFriends;
  1514.             MapSettings.names = configData.MapMarkers.ShowMarkerNames;
  1515.             MapSettings.minimap = configData.MapOptions.MinimapOptions.UseMinimap;
  1516.             MapSettings.complexmap = configData.MapOptions.MinimapOptions.ComplexOptions.UseComplexMap;
  1517.             MapSettings.forcedzoom = configData.MapOptions.MinimapOptions.ComplexOptions.ForceMapZoom;
  1518.             MapSettings.zoomlevel = configData.MapOptions.MinimapOptions.ComplexOptions.ForcedZoomLevel;
  1519.  
  1520.             if (MapSettings.zoomlevel < 1)
  1521.                 MapSettings.zoomlevel = 1;
  1522.             if (MapSettings.zoomlevel > 3)
  1523.                 MapSettings.zoomlevel = 3;
  1524.         }        
  1525.         private void FindStaticMarkers()
  1526.         {
  1527.             if (MapSettings.monuments)
  1528.             {
  1529.                 var monuments = UnityEngine.Object.FindObjectsOfType<MonumentInfo>();
  1530.                 foreach (var monument in monuments)
  1531.                 {
  1532.                     MapMarker mon = new MapMarker
  1533.                     {
  1534.                         x = GetPosition(monument.transform.position.x),
  1535.                         z = GetPosition(monument.transform.position.z)
  1536.                     };
  1537.  
  1538.                     if (monument.name.Contains("lighthouse"))
  1539.                     {
  1540.                         mon.name = msg("lighthouse");
  1541.                         mon.icon = "lighthouse";
  1542.                         staticMarkers.Add(mon);
  1543.                         continue;
  1544.                     }
  1545.                     if (monument.Type == MonumentType.Cave && MapSettings.caves)
  1546.                     {
  1547.                         mon.name = msg("cave");
  1548.                         mon.icon = "cave";
  1549.                         staticMarkers.Add(mon);
  1550.                         continue;
  1551.                     }
  1552.                     if (monument.name.Contains("powerplant_1"))
  1553.                     {
  1554.                         mon.name = msg("powerplant");
  1555.                         mon.icon = "special";
  1556.                         staticMarkers.Add(mon);
  1557.                         continue;
  1558.                     }
  1559.  
  1560.                     if (monument.name.Contains("military_tunnel_1"))
  1561.                     {
  1562.                         mon.name = msg("militarytunnel");
  1563.                         mon.icon = "special";
  1564.                         staticMarkers.Add(mon);
  1565.                         continue;
  1566.                     }
  1567.  
  1568.                     if (monument.name.Contains("airfield_1"))
  1569.                     {
  1570.                         mon.name = msg("airfield");
  1571.                         mon.icon = "special";
  1572.                         staticMarkers.Add(mon);
  1573.                         continue;
  1574.                     }
  1575.  
  1576.                     if (monument.name.Contains("trainyard_1"))
  1577.                     {
  1578.                         mon.name = msg("trainyard");
  1579.                         mon.icon = "special";
  1580.                         staticMarkers.Add(mon);
  1581.                         continue;
  1582.                     }
  1583.  
  1584.                     if (monument.name.Contains("water_treatment_plant_1"))
  1585.                     {
  1586.                         mon.name = msg("waterplant");
  1587.                         mon.icon = "special";
  1588.                         staticMarkers.Add(mon);
  1589.                         continue;
  1590.                     }
  1591.  
  1592.                     if (monument.name.Contains("warehouse"))
  1593.                     {
  1594.                         mon.name = msg("warehouse");
  1595.                         mon.icon = "warehouse";
  1596.                         staticMarkers.Add(mon);
  1597.                         continue;
  1598.                     }
  1599.  
  1600.                     if (monument.name.Contains("satellite_dish"))
  1601.                     {
  1602.  
  1603.                         mon.name = msg("dish");
  1604.                         mon.icon = "dish";
  1605.                         staticMarkers.Add(mon);
  1606.                         continue;
  1607.                     }
  1608.  
  1609.                     if (monument.name.Contains("sphere_tank"))
  1610.                     {
  1611.                         mon.name = msg("spheretank");
  1612.                         mon.icon = "spheretank";
  1613.                         staticMarkers.Add(mon);
  1614.                         continue;
  1615.                     }
  1616.  
  1617.                     if (monument.name.Contains("radtown_small_3"))
  1618.                     {
  1619.                         mon.name = msg("radtown");
  1620.                         mon.icon = "radtown";
  1621.                         staticMarkers.Add(mon);
  1622.                         continue;
  1623.                     }
  1624.                 }
  1625.             }                      
  1626.         }
  1627.         static double GrabCurrentTime() => DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
  1628.         #endregion
  1629.  
  1630.         #region Helpers
  1631.         public static string RemoveSpecialCharacters(string str)
  1632.         {
  1633.             StringBuilder sb = new StringBuilder();
  1634.             foreach (char c in str)
  1635.             {
  1636.                 if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= 'А' && c <= 'Я') || (c >= 'а' && c <= 'я') || c == '.' || c == '_')
  1637.                 {
  1638.                     sb.Append(c);
  1639.                 }
  1640.             }
  1641.             return sb.ToString();
  1642.         }
  1643.         static float GetPosition(float pos) => (pos + mapSize / 2f) / mapSize;  
  1644.         static int GetDirection(float rotation) => (int)((rotation - 5) / 10 + 0.5) * 10;
  1645.         #endregion
  1646.  
  1647.         #region API
  1648.         void EnableMaps(BasePlayer player)
  1649.         {
  1650.             var user = GetUser(player);
  1651.             if (user != null)
  1652.                 user.EnableUser();
  1653.         }
  1654.         void DisableMaps(BasePlayer player)
  1655.         {
  1656.             var user = GetUser(player);
  1657.             if (user != null)
  1658.                 user.DisableUser();
  1659.         }
  1660.  
  1661.         #region Markers
  1662.         bool AddMarker(float x, float z, string name, string icon = "special", float r = 0)
  1663.         {
  1664.             if (customMarkers.ContainsKey(name)) return false;
  1665.             MapMarker marker = new MapMarker
  1666.             {
  1667.                 icon = icon,
  1668.                 name = name,
  1669.                 x = GetPosition(x),
  1670.                 z = GetPosition(z),
  1671.                 r = r
  1672.             };
  1673.             if (r > 0) marker.r = GetDirection(r);
  1674.  
  1675.             customMarkers.Add(name, marker);
  1676.             if (!string.IsNullOrEmpty(icon) && icon != "special")
  1677.                 assets.Add(icon, $"{dataDirectory}custom{Path.DirectorySeparatorChar}{icon}");            
  1678.             SaveMarkers();
  1679.             return true;
  1680.         }
  1681.         void UpdateMarker(float x, float z, string name, string icon = "special", float r = 0)
  1682.         {
  1683.             if (!customMarkers.ContainsKey(name)) return;
  1684.             MapMarker marker = new MapMarker
  1685.             {
  1686.                 icon = icon,
  1687.                 name = name,
  1688.                 x = GetPosition(x),
  1689.                 z = GetPosition(z),
  1690.                 r = r
  1691.             };
  1692.             if (r > 0) marker.r = GetDirection(r);
  1693.             customMarkers[name] = marker;
  1694.  
  1695.             if (!string.IsNullOrEmpty(icon) && icon != "special")
  1696.                 assets.Add(icon, $"{dataDirectory}custom{Path.DirectorySeparatorChar}{icon}");
  1697.             SaveMarkers();
  1698.         }
  1699.         bool RemoveMarker(string name)
  1700.         {
  1701.             if (!customMarkers.ContainsKey(name)) return false;
  1702.             customMarkers.Remove(name);
  1703.             SaveMarkers();
  1704.             return true;
  1705.         }
  1706.         #endregion
  1707.  
  1708.         #region Friends
  1709.         bool AddFriendList(string playerId, string name, List<string> list, bool bypass = false)
  1710.         {
  1711.             if (!bypass && !configData.FriendOptions.AllowCustomLists) return false;
  1712.             var user = GetUserByID(playerId);
  1713.             if (user == null) return false;
  1714.             if (user.HasFriendList(name))
  1715.                 return false;
  1716.  
  1717.             user.AddFriendList(name, list);
  1718.             return true;
  1719.         }
  1720.         bool RemoveFriendList(string playerId, string name, bool bypass = false)
  1721.         {
  1722.             if (!bypass && !configData.FriendOptions.AllowCustomLists) return false;
  1723.             var user = GetUserByID(playerId);
  1724.             if (user == null) return false;
  1725.             if (!user.HasFriendList(name))
  1726.                 return false;
  1727.  
  1728.             user.RemoveFriendList(name);
  1729.             return true;
  1730.         }
  1731.         bool UpdateFriendList(string playerId, string name, List<string> list, bool bypass = false)
  1732.         {
  1733.             if (!bypass && !configData.FriendOptions.AllowCustomLists) return false;
  1734.             var user = GetUserByID(playerId);
  1735.             if (user == null) return false;
  1736.             if (!user.HasFriendList(name))
  1737.                 return false;
  1738.  
  1739.             user.UpdateFriendList(name, list);
  1740.             return true;
  1741.         }
  1742.         bool AddFriend(string playerId, string name, string friendId, bool bypass = false)
  1743.         {
  1744.             if (!bypass && !configData.FriendOptions.AllowCustomLists) return false;
  1745.             var user = GetUserByID(playerId);
  1746.             if (user == null) return false;
  1747.             if (!user.HasFriendList(name))
  1748.                 user.AddFriendList(name, new List<string>());
  1749.             if (user.HasFriend(name, friendId))
  1750.                 return true;
  1751.             user.AddFriend(name, friendId);
  1752.             return true;
  1753.         }
  1754.         bool RemoveFriend(string playerId, string name, string friendId, bool bypass = false)
  1755.         {
  1756.             if (!bypass && !configData.FriendOptions.AllowCustomLists) return false;
  1757.             var user = GetUserByID(playerId);
  1758.             if (user == null) return false;
  1759.             if (!user.HasFriendList(name))
  1760.                 return false;
  1761.             if (!user.HasFriend(name, friendId))
  1762.                 return true;
  1763.             user.RemoveFriend(name, friendId);
  1764.             return true;
  1765.         }
  1766.         #endregion
  1767.  
  1768.         #endregion
  1769.  
  1770.         #region External API  
  1771.         void JoinedEvent(BasePlayer player)
  1772.         {
  1773.             var user = GetUser(player);
  1774.             if (user != null)
  1775.                 user.EnterEvent();
  1776.         }
  1777.         void LeftEvent(BasePlayer player)
  1778.         {
  1779.             var user = GetUser(player);
  1780.             if (user != null)
  1781.                 user.ExitEvent();
  1782.         }
  1783.  
  1784.         #region Friends
  1785.         List<string> GetFriends(ulong playerId)
  1786.         {
  1787.             if (Friends != null)
  1788.             {
  1789.                 if (isRustFriends)
  1790.                     return GetRustFriends(playerId);
  1791.                 return GetUniversalFriends(playerId);
  1792.             }
  1793.             return new List<string>();
  1794.         }
  1795.         List<string> GetRustFriends(ulong playerId)
  1796.         {
  1797.             var list = new List<string>();
  1798.             var success = Friends?.Call("IsFriendOfS", playerId.ToString());
  1799.             if (success is string[])
  1800.             {
  1801.                 return (success as string[]).ToList();
  1802.             }
  1803.             return list;
  1804.         }
  1805.         List<string> GetUniversalFriends(ulong playerId)
  1806.         {
  1807.             var success = Friends?.Call("GetFriendsReverse", playerId.ToString());
  1808.             if (success is string[])
  1809.             {
  1810.                 return (success as string[]).ToList();
  1811.             }
  1812.             return new List<string>();
  1813.         }
  1814.         void OnFriendAdded(object playerId, object friendId)
  1815.         {
  1816.             AddFriend(friendId.ToString(), "FriendsAPI", playerId.ToString(), true);
  1817.         }
  1818.         void OnFriendRemoved(object playerId, object friendId)
  1819.         {
  1820.             RemoveFriend(friendId.ToString(), "FriendsAPI", playerId.ToString(), true);
  1821.         }
  1822.         #endregion
  1823.  
  1824.         #region Clans
  1825.         void GetClans()
  1826.         {
  1827.             if (Clans)
  1828.             {
  1829.                 var allClans = Clans?.Call("GetAllClans");
  1830.                 if (allClans != null && allClans is JArray)
  1831.                 {
  1832.                     foreach(var clan in (JArray)allClans)
  1833.                     {
  1834.                         var name = clan.ToString();
  1835.                         List<string> members = GetClanMembers(name);
  1836.                         if (!clanData.ContainsKey(name))
  1837.                             clanData.Add(name, new List<string>());
  1838.                         clanData[name] = members;
  1839.                     }
  1840.                 }
  1841.             }
  1842.         }
  1843.         string GetClan(ulong playerId)
  1844.         {
  1845.             string clanName = Clans?.Call<string>("GetClanOf", playerId);
  1846.             if (!string.IsNullOrEmpty(clanName))
  1847.             {
  1848.                 if (!clanData.ContainsKey(clanName))
  1849.                     clanData.Add(clanName, GetClanMembers(clanName));
  1850.             }
  1851.             return clanName;
  1852.         }
  1853.         List<string> GetClanMembers(string clanTag)
  1854.         {
  1855.             var newList = new List<string>();
  1856.             var clan = instance.Clans?.Call("GetClan", clanTag);
  1857.             if (clan != null && clan is JObject)
  1858.             {                
  1859.                 var members = (clan as JObject).GetValue("members");
  1860.                 if (members != null && members is JArray)
  1861.                 {
  1862.                     foreach (var member in (JArray)members)
  1863.                     {
  1864.                         newList.Add(member.ToString());
  1865.                     }
  1866.                 }
  1867.             }
  1868.             return newList;
  1869.         }
  1870.         void OnClanCreate(string tag)
  1871.         {
  1872.             if (!clanData.ContainsKey(tag))
  1873.                 clanData.Add(tag, GetClanMembers(tag));
  1874.         }
  1875.         void OnClanUpdate(string tag)
  1876.         {
  1877.             var members = GetClanMembers(tag);
  1878.             if (!clanData.ContainsKey(tag))                            
  1879.                 clanData.Add(tag, members);            
  1880.             else
  1881.             {
  1882.                 foreach (var member in clanData[tag])                                    
  1883.                     RemoveFriendList(member, "Clans", true);
  1884.                 foreach(var member in members)
  1885.                     AddFriendList(member, "Clans", members, true);
  1886.                 clanData[tag] = members;
  1887.             }            
  1888.         }
  1889.         void OnClanDestroy(string tag)
  1890.         {            
  1891.             if (clanData.ContainsKey(tag))
  1892.             {
  1893.                 foreach(var member in clanData[tag])                                    
  1894.                     RemoveFriendList(member, "Clans", true);                
  1895.                 clanData.Remove(tag);
  1896.             }
  1897.         }
  1898.         #endregion
  1899.         #endregion
  1900.  
  1901.         #region Config        
  1902.         private ConfigData configData;
  1903.         class FriendLists
  1904.         {
  1905.             public bool AllowCustomLists { get; set; }
  1906.             public bool UseClans { get; set; }
  1907.             public bool UseFriends { get; set; }            
  1908.         }
  1909.         class MapMarkers
  1910.         {
  1911.             public bool ShowAllPlayers { get; set; }
  1912.             public bool ShowCaves { get; set; }
  1913.             public bool ShowDebris { get; set; }
  1914.             public bool ShowFriends { get; set; }
  1915.             public bool ShowHelicopters { get; set; }
  1916.             public bool ShowMarkerNames { get; set; }
  1917.             public bool ShowMonuments { get; set; }
  1918.             public bool ShowPlanes { get; set; }
  1919.             public bool ShowPlayer { get; set; }
  1920.             public bool ShowSupplyDrops { get; set; }
  1921.         }
  1922.         class MapOptions
  1923.         {
  1924.             public bool HideEventPlayers { get; set; }
  1925.             public string MapKeybind { get; set; }
  1926.             public bool StartOpen { get; set; }
  1927.             public bool ShowCompass { get; set; }
  1928.             public MapImages MapImage { get; set; }
  1929.             public Minimap MinimapOptions { get; set; }
  1930.             public float UpdateSpeed { get; set; }          
  1931.         }
  1932.         class MapImages
  1933.         {
  1934.             public string APIKey { get; set; }
  1935.             public bool CustomMap_Use { get; set; }
  1936.             public string CustomMap_Filename { get; set; }
  1937.         }
  1938.         class Minimap
  1939.         {            
  1940.             public bool UseMinimap { get; set; }
  1941.             public float HorizontalScale { get; set; }
  1942.             public float VerticalScale { get; set; }
  1943.             public bool OnLeftSide { get; set; }
  1944.             public float OffsetSide { get; set; }
  1945.             public float OffsetTop { get; set; }
  1946.             public ComplexMap ComplexOptions { get; set; }
  1947.         }
  1948.         class ComplexMap
  1949.         {
  1950.             public bool UseComplexMap { get; set; }
  1951.             public bool ForceMapZoom { get; set; }
  1952.             public int ForcedZoomLevel { get; set; }
  1953.         }
  1954.         class SpamOptions
  1955.         {
  1956.             public int TimeBetweenAttempts { get; set; }
  1957.             public int WarningAttempts { get; set; }
  1958.             public int DisableAttempts { get; set; }
  1959.             public int DisableSeconds { get; set; }
  1960.             public bool Enabled { get; set; }
  1961.         }
  1962.         class ConfigData
  1963.         {
  1964.             public FriendLists FriendOptions { get; set; }
  1965.             public MapMarkers MapMarkers { get; set; }
  1966.             public MapOptions MapOptions { get; set; }
  1967.             public SpamOptions SpamOptions { get; set; }
  1968.         }
  1969.         private void LoadVariables()
  1970.         {
  1971.             LoadConfigVariables();
  1972.             SaveConfig();
  1973.         }
  1974.         protected override void LoadDefaultConfig()
  1975.         {
  1976.             var config = new ConfigData
  1977.             {
  1978.                 FriendOptions = new FriendLists
  1979.                 {
  1980.                     AllowCustomLists = true,
  1981.                     UseClans = true,
  1982.                     UseFriends = true,
  1983.                 },
  1984.                 MapMarkers = new MapMarkers
  1985.                 {
  1986.                     ShowAllPlayers = false,
  1987.                     ShowCaves = false,
  1988.                     ShowDebris = false,
  1989.                     ShowFriends = true,
  1990.                     ShowHelicopters = true,
  1991.                     ShowMarkerNames = true,
  1992.                     ShowMonuments = true,
  1993.                     ShowPlanes = true,
  1994.                     ShowPlayer = true,
  1995.                     ShowSupplyDrops = true
  1996.                 },
  1997.                 MapOptions = new MapOptions
  1998.                 {                    
  1999.                     HideEventPlayers = true,
  2000.                     MapKeybind = "m",
  2001.                     ShowCompass = true,
  2002.                     StartOpen = true,
  2003.                     MapImage = new MapImages
  2004.                     {
  2005.                         APIKey = "",
  2006.                         CustomMap_Filename = "Map35001234567.png",
  2007.                         CustomMap_Use = true
  2008.                     },
  2009.                     MinimapOptions = new Minimap
  2010.                     {
  2011.                         ComplexOptions = new ComplexMap
  2012.                         {
  2013.                             ForcedZoomLevel = 1,
  2014.                             ForceMapZoom = false,
  2015.                             UseComplexMap = true
  2016.                         },
  2017.                         HorizontalScale = 1.0f,
  2018.                         VerticalScale = 1.0f,
  2019.                         OnLeftSide = true,
  2020.                         OffsetSide = 0,
  2021.                         OffsetTop = 0,
  2022.                         UseMinimap = true
  2023.                     },
  2024.                     UpdateSpeed = 1f
  2025.                 },
  2026.                 SpamOptions = new SpamOptions
  2027.                 {
  2028.                     DisableAttempts = 10,
  2029.                     DisableSeconds = 120,
  2030.                     Enabled = true,
  2031.                     TimeBetweenAttempts = 3,
  2032.                     WarningAttempts = 5
  2033.                 }
  2034.                              
  2035.             };
  2036.             SaveConfig(config);
  2037.         }
  2038.         private void LoadConfigVariables() => configData = Config.ReadObject<ConfigData>();
  2039.         void SaveConfig(ConfigData config) => Config.WriteObject(config, true);
  2040.         #endregion
  2041.  
  2042.         #region Data Management
  2043.         void SaveData()
  2044.         {
  2045.             imageData.WriteObject(storedImages);            
  2046.         }
  2047.         void SaveMarkers()
  2048.         {
  2049.             markerData.WriteObject(storedMarkers);
  2050.             SaveData();
  2051.         }
  2052.         void LoadData()
  2053.         {
  2054.             try
  2055.             {
  2056.                 storedImages = imageData.ReadObject<ImageStore>();
  2057.                
  2058.             }
  2059.             catch
  2060.             {
  2061.                 storedImages = new ImageStore();
  2062.             }
  2063.             try
  2064.             {
  2065.                 storedMarkers = markerData.ReadObject<MarkerData>();
  2066.                 customMarkers = storedMarkers.data;
  2067.             }
  2068.             catch
  2069.             {
  2070.                 storedMarkers = new MarkerData();
  2071.             }
  2072.         }
  2073.         class ImageStore
  2074.         {
  2075.             public Dictionary<string, uint> data = new Dictionary<string, uint>();
  2076.         }
  2077.         class MarkerData
  2078.         {
  2079.             public Dictionary<string, MapMarker> data = new Dictionary<string, MapMarker>();
  2080.         }
  2081.         #endregion
  2082.  
  2083.         #region Image Storage
  2084.         private string GetImage(string name)
  2085.         {
  2086.             if (string.IsNullOrEmpty(name)) return null;
  2087.             if (storedImages.data.ContainsKey(name))
  2088.                 return storedImages.data[name].ToString();            
  2089.             else return null;
  2090.         }
  2091.         class ImageAssets : MonoBehaviour
  2092.         {
  2093.             LustyMap filehandler;
  2094.             private Queue<QueueItem> QueueList = new Queue<QueueItem>();
  2095.             private MemoryStream stream = new MemoryStream();
  2096.             const int MaxActiveLoads = 3;            
  2097.             static byte activeLoads;            
  2098.  
  2099.             private void Awake() => filehandler = (LustyMap)Interface.Oxide.RootPluginManager.GetPlugin(nameof(LustyMap));                        
  2100.             private void OnDestroy()
  2101.             {
  2102.                 QueueList.Clear();
  2103.                 filehandler = null;
  2104.             }
  2105.             public void Add(string name, string url)
  2106.             {
  2107.                 QueueList.Enqueue(new QueueItem(url, name));
  2108.                 if (activeLoads < MaxActiveLoads) Next();
  2109.             }
  2110.             void Next()
  2111.             {
  2112.                 if (QueueList.Count <= 0) return;
  2113.                 activeLoads++;
  2114.                 StartCoroutine(WaitForRequest(QueueList.Dequeue()));
  2115.             }
  2116.             private void ClearStream()
  2117.             {
  2118.                 stream.Position = 0;
  2119.                 stream.SetLength(0);
  2120.             }
  2121.  
  2122.             IEnumerator WaitForRequest(QueueItem info)
  2123.             {
  2124.                 using (var www = new WWW(info.url))
  2125.                 {
  2126.                     yield return www;
  2127.                     if (filehandler == null) yield break;
  2128.                     if (www.error != null)
  2129.                     {
  2130.                         print(string.Format("Image loading fail! Error: {0}", www.error));
  2131.                     }
  2132.                     else
  2133.                     {
  2134.                         ClearStream();
  2135.                         stream.Write(www.bytes, 0, www.bytes.Length);
  2136.                         uint textureID = FileStorage.server.Store(stream, FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID);                        
  2137.                         ClearStream();
  2138.                         if (!filehandler.storedImages.data.ContainsKey(info.name))
  2139.                             filehandler.storedImages.data.Add(info.name, textureID);
  2140.                         else
  2141.                             filehandler.storedImages.data[info.name] = textureID;
  2142.                     }
  2143.                     activeLoads--;
  2144.                     if (QueueList.Count > 0) Next();
  2145.                     else if (QueueList.Count <= 0) filehandler.SaveData();
  2146.                 }
  2147.             }
  2148.             internal class QueueItem
  2149.             {
  2150.                 public string url;
  2151.                 public string name;
  2152.                 public QueueItem(string url, string name)
  2153.                 {
  2154.                     this.url = url;
  2155.                     this.name = name;
  2156.                 }
  2157.             }
  2158.         }
  2159.         void ValidateImages()
  2160.         {
  2161.             PrintWarning("Validating imagery");
  2162.             if (isNewSave || storedImages.data.Count < 194)
  2163.                 LoadImages();
  2164.             else
  2165.             {
  2166.                 PrintWarning("Images and icons found in server storage");
  2167.             }
  2168.             if (string.IsNullOrEmpty(GetImage("mapimage")))
  2169.             {
  2170.                 LoadMapImage();
  2171.             }
  2172.             else GenerateMaps(true, MapSettings.minimap, MapSettings.complexmap);
  2173.         }
  2174.         private void LoadImages()
  2175.         {
  2176.             PrintWarning("Icon images have not been found. Uploading images to file storage");
  2177.  
  2178.             string[] files = new string[] { "self", "friend", "other", "heli", "plane" };
  2179.             string path = $"{dataDirectory}icons{Path.DirectorySeparatorChar}";
  2180.  
  2181.             foreach (string file in files)
  2182.             {                
  2183.                 string ext = ".png";
  2184.                 for (int i = 0; i <= 360; i = i + 10)                
  2185.                     assets.Add(file + i, path + file + i + ext);                
  2186.             }
  2187.            
  2188.             assets.Add("lighthouse", $"{path}lighthouse.png");
  2189.             assets.Add("radtown", $"{path}radtown.png");
  2190.             assets.Add("cave", $"{path}cave.png");
  2191.             assets.Add("warehouse", $"{path}warehouse.png");
  2192.             assets.Add("dish", $"{path}dish.png");
  2193.             assets.Add("spheretank", $"{path}spheretank.png");
  2194.             assets.Add("special", $"{path}special.png");
  2195.             assets.Add("supply", $"{path}supply.png");
  2196.             assets.Add("debris", $"{path}debris.png");
  2197.  
  2198.             foreach (var image in customMarkers)
  2199.             {
  2200.                 if (image.Value.icon != "special")                    
  2201.                     assets.Add(image.Value.icon, dataDirectory + "custom" + Path.DirectorySeparatorChar + image.Value.icon);
  2202.             }          
  2203.         }
  2204.         private void LoadMapImage()
  2205.         {
  2206.             if (configData.MapOptions.MapImage.CustomMap_Use)
  2207.             {
  2208.                 PrintWarning("Downloading map image to file storage. Please wait!");
  2209.                 assets.Add("mapimage", dataDirectory + configData.MapOptions.MapImage.CustomMap_Filename);
  2210.                 if (MapSettings.complexmap)
  2211.                 {
  2212.                     PrintWarning("Attempting to split and store the complex mini-map. This may take a few moments! Failure to wait for this process to finish WILL result in error!");
  2213.                     AttemptSplit();
  2214.                 }
  2215.                 else GenerateMaps(true, MapSettings.minimap, false);
  2216.             }
  2217.             else DownloadMapImage();
  2218.         }      
  2219.         #endregion
  2220.  
  2221.         #region Map Generation - Credits to Calytic, Nogrod, kraz and beancan.io for the awesome looking map images and API to make this possible!
  2222.         void DownloadMapImage()
  2223.         {
  2224.             if (string.IsNullOrEmpty(configData.MapOptions.MapImage.APIKey))
  2225.             {
  2226.                 PrintError("You must supply a valid API key to utilize the auto-download feature!\nVisit 'beancan.io' and register your server to retrieve your API key!");
  2227.                 activated = false;
  2228.                 return;
  2229.             }
  2230.             PrintWarning("Attempting to contact beancan.io to download your map image!");
  2231.             GetQueueID();            
  2232.         }
  2233.         void GetQueueID()
  2234.         {
  2235.             var url = $"http://beancan.io/map-queue-generate?level={level}&seed={mapSeed}&size={mapSize}&key={configData.MapOptions.MapImage.APIKey}";
  2236.             webrequest.EnqueueGet(url, (code, response) =>
  2237.             {
  2238.                 if (code != 200 || string.IsNullOrEmpty(response))
  2239.                 {
  2240.                     if (code == 403)
  2241.                         PrintError($"Error: {code} - Invalid API key. Unable to download map image");
  2242.                     else PrintWarning($"Error: {code} - Couldn't get an answer from beancan.io. Unable to download map image. Please try again in a few minutes");                    
  2243.                 }
  2244.                 else CheckAvailability(response);
  2245.             }, this);
  2246.         }
  2247.         void CheckAvailability(string queueId)
  2248.         {
  2249.             webrequest.EnqueueGet($"http://beancan.io/map-queue/{queueId}", (code, response) =>
  2250.             {
  2251.                 if (string.IsNullOrEmpty(response))
  2252.                 {
  2253.                     PrintWarning($"Error: {code} - Couldn't get an answer from beancan.io");
  2254.                 }
  2255.                 else ProcessResponse(queueId, response);
  2256.             }, this);
  2257.         }
  2258.         void ProcessResponse(string queueId, string response)
  2259.         {
  2260.             switch (response)
  2261.             {
  2262.                 case "-1":
  2263.                     PrintWarning("Your map is still in the queue to be generated. Checking again in 10 seconds");
  2264.                     break;
  2265.                 case "0":
  2266.                     PrintWarning("Your map is still being generated. Checking again in 10 seconds");
  2267.                     break;
  2268.                 case "1":
  2269.                     GetMapURL(queueId);
  2270.                     return;
  2271.                 default:
  2272.                     PrintWarning($"Error retrieving map: Invalid response from beancan.io: Response code {response}");
  2273.                     return;
  2274.             }
  2275.             timer.Once(10, () => CheckAvailability(queueId));
  2276.         }
  2277.         void GetMapURL(string queueId)
  2278.         {
  2279.             var url = $"http://beancan.io/map-queue-image/{queueId}";
  2280.             webrequest.EnqueueGet(url, (code, response) =>
  2281.             {
  2282.                 if (string.IsNullOrEmpty(response))
  2283.                 {
  2284.                     PrintWarning($"Error: {code} - Couldn't get an answer from beancan.io");
  2285.                 }
  2286.                 else DownloadMap(response);
  2287.             }, this);
  2288.         }
  2289.         void DownloadMap(string url)
  2290.         {
  2291.             PrintWarning("Map generation successful! Downloading map image to file storage. Please wait!");
  2292.             assets.Add("mapimage", url);
  2293.             if (MapSettings.complexmap)
  2294.             {
  2295.                 PrintWarning("Attempting to split and store the complex mini-map. This may take a while, please wait!");
  2296.                 AttemptSplit();
  2297.             }
  2298.             else GenerateMaps(true, MapSettings.minimap, false);            
  2299.         }
  2300.         #endregion
  2301.  
  2302.         #region Map Splitter
  2303.         void AttemptSplit(int attempts = 0)
  2304.         {
  2305.             if (attempts == 5)
  2306.             {
  2307.                 PrintError("The plugin has timed out trying to find the map image to split! Complex map has been disabled");
  2308.                 MapSettings.complexmap = false;                
  2309.                 return;
  2310.             }
  2311.             if (storedImages.data.ContainsKey("mapimage"))
  2312.             {
  2313.                 var imageId = storedImages.data["mapimage"];
  2314.                 if (mapSplitter.SplitMap(imageId))
  2315.                 {
  2316.                     PrintWarning("Map split was successful!");
  2317.                     GenerateMaps(true, MapSettings.minimap, true);
  2318.                 }
  2319.                 else
  2320.                 {
  2321.                     MapSettings.complexmap = false;
  2322.                     GenerateMaps(true, MapSettings.minimap, MapSettings.complexmap);
  2323.                 }
  2324.             }
  2325.             else
  2326.             {
  2327.                 PrintWarning($"Map image not found in file store. Waiting for 10 seconds and trying again (Attempt: {attempts + 1} / 5)");
  2328.                 timer.Once(10, () => AttemptSplit(attempts + 1));
  2329.             }
  2330.         }        
  2331.         class MapSplitter : MonoBehaviour
  2332.         {
  2333.             LustyMap filehandler;
  2334.             private Queue<QueueItem> QueueList = new Queue<QueueItem>();
  2335.             private MemoryStream stream = new MemoryStream();
  2336.             const int MaxActiveLoads = 3;
  2337.             static byte activeLoads;
  2338.  
  2339.             internal class QueueItem
  2340.             {
  2341.                 public byte[] bmp;
  2342.                 public string name;
  2343.                 public QueueItem(byte[] bmp, string name)
  2344.                 {
  2345.                     this.bmp = bmp;
  2346.                     this.name = name;
  2347.                 }
  2348.             }
  2349.             private void Awake() => filehandler = (LustyMap)Interface.Oxide.RootPluginManager.GetPlugin(nameof(LustyMap));
  2350.             private void OnDestroy()
  2351.             {
  2352.                 QueueList.Clear();
  2353.                 filehandler = null;
  2354.             }
  2355.  
  2356.             public bool SplitMap(uint imageId)
  2357.             {
  2358.                 System.Drawing.Image img = ImageFromStorage(imageId);
  2359.                 if (img == null)
  2360.                 {
  2361.                     instance.PrintError("There was a error retrieving the map image from file storage. This may be caused by slow processing of the images being uploaded to your server. Please wait a few minutes and try again");
  2362.                     return false;
  2363.                 }
  2364.                 foreach (var amount in new List<int> { 6, 12, 26 })//, 32 })
  2365.                 {
  2366.                     int width = (int)(img.Width / (double)amount);
  2367.                     int height = (int)(img.Height / (double)amount);
  2368.  
  2369.                     int rowCount = 0;
  2370.                     int colCount = 0;
  2371.                     for (int r = 0; r < amount; r++)
  2372.                     {
  2373.                         colCount = 0;
  2374.                         for (int c = 0; c < amount; c++)
  2375.                         {
  2376.                             var column = colCount;
  2377.                             var row = rowCount;
  2378.                             if (column < 1) column = 1;
  2379.                             if (column > amount - 2) column = amount - 2;
  2380.                             if (row < 1) row = 1;
  2381.                             if (row > amount - 2) row = amount - 2;
  2382.                            
  2383.                             Bitmap cutPiece = new Bitmap(width * 3, height * 3);
  2384.                             System.Drawing.Graphics graphic = System.Drawing.Graphics.FromImage(cutPiece);
  2385.                             graphic.DrawImage(img, new Rectangle(0, 0, width * 3, height * 3), new Rectangle((width * column) - width, (height * row) - height, width * 3, height * 3), GraphicsUnit.Pixel);
  2386.                             graphic.Dispose();
  2387.                             colCount++;
  2388.  
  2389.                             StoreImagePiece(cutPiece, $"map-{amount}-{r}-{c}");                            
  2390.                         }
  2391.                         rowCount++;
  2392.                     }
  2393.                 }                
  2394.                 return true;
  2395.             }            
  2396.             private System.Drawing.Image ImageFromStorage(uint imageId)
  2397.             {
  2398.                 byte[] imageData = FileStorage.server.Get(imageId, FileStorage.Type.png, 0U);
  2399.                 System.Drawing.Image img = null;
  2400.                 try
  2401.                 {
  2402.                     img = (System.Drawing.Bitmap)((new System.Drawing.ImageConverter()).ConvertFrom(imageData));
  2403.                 }
  2404.                 catch (Exception ex)
  2405.                 {
  2406.                     instance.PrintError($"Error whilst retrieving the map image from file storage: {ex.Message}\nIf you are running linux you must install LibGDIPlus using the following line: \"sudo apt install libgdiplus\", then restart your system for the changes to take affect");
  2407.                 }
  2408.                 return img;
  2409.             }        
  2410.             internal void StoreImagePiece(System.Drawing.Bitmap bmp, string name)
  2411.             {
  2412.                 System.Drawing.ImageConverter converter = new System.Drawing.ImageConverter();
  2413.                 byte[] array = (byte[])converter.ConvertTo(bmp, typeof(byte[]));
  2414.                 Add(name, array);
  2415.             }
  2416.  
  2417.             internal void Add(string name, byte[] bmp)
  2418.             {
  2419.                 QueueList.Enqueue(new QueueItem(bmp, name));
  2420.                 if (activeLoads < MaxActiveLoads) Next();
  2421.             }
  2422.             internal void Next()
  2423.             {
  2424.                 if (QueueList.Count <= 0) return;
  2425.                 activeLoads++;
  2426.                 StartCoroutine(StoreNextSplit(QueueList.Dequeue()));
  2427.             }
  2428.             internal void ClearStream()
  2429.             {
  2430.                 stream.Position = 0;
  2431.                 stream.SetLength(0);
  2432.             }            
  2433.            
  2434.             IEnumerator StoreNextSplit(QueueItem info)
  2435.             {
  2436.                 if (filehandler == null) yield break;
  2437.                 if (info.bmp == null)
  2438.                 {
  2439.                     instance.PrintError($"Error whilst storing map piece to file storage : {info.name}");                    
  2440.                 }
  2441.                 else
  2442.                 {
  2443.                     ClearStream();
  2444.                     stream.Write(info.bmp, 0, info.bmp.Length);
  2445.                     uint textureID = FileStorage.server.Store(stream, FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID);
  2446.                     ClearStream();
  2447.  
  2448.                     if (!filehandler.storedImages.data.ContainsKey(info.name))
  2449.                         filehandler.storedImages.data.Add(info.name, textureID);
  2450.                     else filehandler.storedImages.data[info.name] = textureID;
  2451.                 }
  2452.                 activeLoads--;
  2453.                 if (QueueList.Count > 0) Next();
  2454.                 else if (QueueList.Count <= 0) filehandler.SaveData();
  2455.  
  2456.             }            
  2457.         }
  2458.         #endregion
  2459.  
  2460.         #region Localization
  2461.         string msg(string key, string playerid = null) => lang.GetMessage(key, this, playerid);
  2462.  
  2463.         Dictionary<string, string> Messages = new Dictionary<string, string>
  2464.         {
  2465.             {"cpsN", "North" },
  2466.             {"cpsNE", "North-East" },
  2467.             {"cpsE", "East" },
  2468.             {"cpsSE", "South-East" },
  2469.             {"cpsS", "South" },
  2470.             {"cpsSW", "South-West" },
  2471.             {"cpsW", "West" },
  2472.             {"cpsNW", "North-West" },
  2473.             {"Plane", "Plane" },
  2474.             {"Supply Drop", "Supply Drop" },
  2475.             {"Helicopter", "Helicopter" },
  2476.             {"Debris", "Debris" },
  2477.             {"lighthouse", "lighthouse" },
  2478.             {"radtown", "radtown" },
  2479.             {"spheretank", "spheretank" },
  2480.             {"dish","dish" },
  2481.             {"warehouse","warehouse" },
  2482.             {"waterplant", "waterplant" },
  2483.             {"trainyard", "trainyard" },
  2484.             {"airfield", "airfield" },
  2485.             {"militarytunnel", "militarytunnel" },
  2486.             {"powerplant", "powerplant" },
  2487.             {"cave", "cave" },
  2488.             {"spamWarning", "Please do not spam the map. If you continue to do so your map will be temporarily disabled" },
  2489.             {"spamDisable", "Your map has been disabled for {0} seconds" },
  2490.             {"spamEnable", "Your map has been enabled" }
  2491.         };
  2492.         #endregion
  2493.     }
  2494. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement