Иконка ресурса

FREE UAVSignal : 1.8.8 1.8.8

Нет прав для скачивания
C#:
// -----------------------------------------------------------------------
// Copyright (c) Yac Vaguer. All rights reserved.
// Author: Yac Vaguer
// This is proprietary software. Unauthorized copying, distribution, or modification is strictly prohibited.
// Licensed for use only on servers owned by the purchaser.
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using UnityEngine;

namespace Oxide.Plugins
{
 [Info("UAVSignal", "Yac Vaguer", "1.8.8")]
 [Description("Call a UAV to detect nearby players and NPCs.")]
 public class UAVSignal : RustPlugin
 {
 #region Fields

 private const string PermissionUse = "uavsignal.use";
 private const string PermissionAdmin = "uavsignal.admin";
 private const string SupplySignalShortname = "supply.signal";
 private const string F15Prefab = "assets/scripts/entity/misc/f15/f15e.prefab";
 private const string TrackedPanelName = "UAVTrackedPanel";
 private const string AdminPanelName = "UAVAdminPanel";
 private const string AdminContentName = "UAVAdminContent";
 private const string TrackedIconImageKey = "uavsignal.tracked.icon";

 [PluginReference]
 private Plugin ImageLibrary;

 private Configuration config;
 private int supplySignalItemId;
 private bool trackedIconLoaded;
 private readonly Dictionary<ulong, UavSession> activeSessions = new Dictionary<ulong, UavSession>();
 private readonly HashSet<BasePlayer> playersWithUI = new HashSet<BasePlayer>();
 private readonly HashSet<LootContainer> processedContainers = new HashSet<LootContainer>();
 private readonly HashSet<ulong> openAdminPanels = new HashSet<ulong>();

 private static readonly (string Name, string Description, string Url)[] OtherPlugins =
 {
 ("Business Core", "VIP Assassination Mission system with full business management.", "https://codefling.com/plugins/business-core-vip-assassination-mission"),
 ("Water Treatment Showdown Event", "PvE/PvP combat event at the Water Treatment monument.", "https://codefling.com/plugins/water-treatment-showdown-event"),
 ("Ferry Terminal Event", "Action-packed event at the Ferry Terminal monument.", "https://codefling.com/plugins/ferry-terminal-event-1"),
 ("Simple Patrol Signal", "Call in a patrol of attack helicopters with a thrown signal.", "https://codefling.com/plugins/simplepatrolsignal"),
 };

 private const string YacVaguerProfileUrl = "https://codefling.com/yac-vaguer";

 private static readonly FieldInfo F15MovePositionField = typeof(F15).GetField(
 "movePosition",
 BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
 );

 private class UavSession
 {
 public BasePlayer Caller;
 public readonly HashSet<BasePlayer> PlayersInRadius = new HashSet<BasePlayer>();
 public readonly HashSet<ulong> LoggedDetections = new HashSet<ulong>();
 public readonly HashSet<ulong> LoggedSkips = new HashSet<ulong>();
 public Timer TickTimer;
 }

 #endregion

 #region Initialization

 private void Init()
 {
 permission.RegisterPermission(PermissionUse, this);
 permission.RegisterPermission(PermissionAdmin, this);
 LoadDefaultMessages();
 }

 private void OnServerInitialized()
 {
 LoadConfigValues();
 var def = ItemManager.FindItemDefinition(SupplySignalShortname);
 supplySignalItemId = def != null ? def.itemid : 0;
 RegisterTrackedIconWithImageLibrary();
 }

 private void OnPluginLoaded(Plugin plugin)
 {
 if (plugin?.Name == "ImageLibrary")
 {
 RegisterTrackedIconWithImageLibrary();
 }
 }

 private void RegisterTrackedIconWithImageLibrary()
 {
 trackedIconLoaded = false;
 if (ImageLibrary == null) return;
 if (config?.UAV == null || string.IsNullOrEmpty(config.UAV.TrackedIconUrl)) return;

 ImageLibrary.Call(
 "AddImage",
 config.UAV.TrackedIconUrl,
 TrackedIconImageKey,
 0UL,
 new Action(() => { trackedIconLoaded = true; })
 );
 }

 private void Unload()
 {
 foreach (var session in activeSessions.Values.ToList())
 {
 DestroyUAV(session, isUnloading: true);
 }
 activeSessions.Clear();

 foreach (var player in playersWithUI.ToList())
 {
 HideTrackedIcon(player);
 }
 processedContainers.Clear();

 foreach (var id in openAdminPanels.ToList())
 {
 var p = BasePlayer.FindByID(id);
 if (p != null && p.IsConnected)
 {
 CuiHelper.DestroyUi(p, AdminPanelName);
 }
 }
 openAdminPanels.Clear();
 }

 #endregion

 #region Commands

 [ChatCommand("uav")]
 private void UAVCommand(BasePlayer player, string command, string[] args)
 {
 if (!HasAdminPermission(player))
 {
 player.ChatMessage(GetMessage("NoPermission", player.UserIDString));
 return;
 }

 if (args.Length == 1)
 {
 List<string> ambiguous;
 var targetPlayer = FindPlayer(args[0], out ambiguous);
 if (targetPlayer != null)
 {
 GiveUAVSignal(targetPlayer);
 player.ChatMessage(
 string.Format(
 GetMessage("UAVGiven", player.UserIDString),
 targetPlayer.displayName
 )
 );
 }
 else if (ambiguous != null)
 {
 player.ChatMessage(
 string.Format(
 GetMessage("AmbiguousPlayer", player.UserIDString),
 string.Join(", ", ambiguous)
 )
 );
 }
 else
 {
 player.ChatMessage(
 string.Format(GetMessage("PlayerNotFound", player.UserIDString), args[0])
 );
 }
 }
 else
 {
 GiveUAVSignal(player);
 player.ChatMessage(GetMessage("UAVReceived", player.UserIDString));
 }
 }

 [ConsoleCommand("uav")]
 private void UAVConsoleCommand(ConsoleSystem.Arg arg)
 {
 var player = arg.Player();

 if (player != null && !HasAdminPermission(player))
 {
 arg.ReplyWith(GetMessage("NoPermission", player.UserIDString));
 return;
 }

 if (arg.Args == null || arg.Args.Length == 0)
 {
 arg.ReplyWith("Usage: uav <player name or ID>");
 return;
 }

 List<string> ambiguous;
 string targetArg = arg.Args[0].ToString();
 var targetPlayer = FindPlayer(targetArg, out ambiguous);
 if (targetPlayer != null)
 {
 GiveUAVSignal(targetPlayer);
 arg.ReplyWith($"UAV signal given to {targetPlayer.displayName}");
 }
 else if (ambiguous != null)
 {
 arg.ReplyWith($"Ambiguous match for '{targetArg}': {string.Join(", ", ambiguous)}");
 }
 else
 {
 arg.ReplyWith($"Player '{targetArg}' not found.");
 }
 }

 [ChatCommand("uav.admin")]
 private void UAVAdminChatCommand(BasePlayer player, string command, string[] args)
 {
 if (!HasAdminPermission(player))
 {
 player.ChatMessage(GetMessage("NoPermission", player.UserIDString));
 return;
 }
 OpenAdminPanel(player, "info");
 }

 [ConsoleCommand("uavadmin.tab")]
 private void CmdAdminTab(ConsoleSystem.Arg arg)
 {
 var player = arg.Player();
 if (player == null || !HasAdminPermission(player)) return;
 OpenAdminPanel(player, arg.GetString(0, "info"));
 }

 [ConsoleCommand("uavadmin.toggle")]
 private void CmdAdminToggle(ConsoleSystem.Arg arg)
 {
 var player = arg.Player();
 if (player == null || !HasAdminPermission(player)) return;

 switch (arg.GetString(0))
 {
 case "debug": config.DebugMode = !config.DebugMode; break;
 case "loot": config.Loot.Enabled = !config.Loot.Enabled; break;
 case "teammates": config.Tracking.Teammates.Enabled = !config.Tracking.Teammates.Enabled; break;
 case "npcs": config.Tracking.NPCs.Enabled = !config.Tracking.NPCs.Enabled; break;
 case "otherplayers": config.Tracking.OtherPlayers.Enabled = !config.Tracking.OtherPlayers.Enabled; break;
 default: return;
 }

 SaveConfig();
 OpenAdminPanel(player, "info");
 }

 [ConsoleCommand("uavadmin.reload")]
 private void CmdAdminReload(ConsoleSystem.Arg arg)
 {
 var player = arg.Player();
 if (player == null || !HasAdminPermission(player)) return;
 LoadConfigValues();
 player.ChatMessage(GetMessage("AdminConfigReloaded", player.UserIDString));
 OpenAdminPanel(player, "info");
 }

 [ConsoleCommand("uavadmin.close")]
 private void CmdAdminClose(ConsoleSystem.Arg arg)
 {
 var player = arg.Player();
 if (player == null) return;
 CloseAdminPanel(player);
 }

 [ConsoleCommand("uavadmin.link")]
 private void CmdAdminLink(ConsoleSystem.Arg arg)
 {
 var player = arg.Player();
 if (player == null || !HasAdminPermission(player)) return;
 int idx = arg.GetInt(0, -1);
 if (idx < 0 || idx >= OtherPlugins.Length) return;
 var p = OtherPlugins[idx];
 player.ChatMessage(string.Format(GetMessage("AdminPluginLink", player.UserIDString), p.Name, p.Url));
 }

 [ConsoleCommand("uavadmin.profile")]
 private void CmdAdminProfile(ConsoleSystem.Arg arg)
 {
 var player = arg.Player();
 if (player == null || !HasAdminPermission(player)) return;
 player.ChatMessage(string.Format(GetMessage("AdminProfileLink", player.UserIDString), YacVaguerProfileUrl));
 }

 #endregion

 #region Event Hooks

 private void OnExplosiveThrown(BasePlayer player, BaseEntity entity)
 {
 if (config == null) return;
 if (entity is SupplySignal signal && signal.skinID == config.UAV.SkinID)
 {
 ThrowUAV(signal, player);
 }
 }

 private void OnExplosiveDropped(BasePlayer player, BaseEntity entity, ThrownWeapon item)
 {
 if (config == null) return;
 if (entity is SupplySignal signal && signal.skinID == config.UAV.SkinID)
 {
 ThrowUAV(signal, player);
 }
 }

 private void ThrowUAV(SupplySignal signal, BasePlayer player)
 {
 if (signal == null || signal.IsDestroyed) return;
 if (player == null) return;

 signal.CancelInvoke(signal.Explode);
 signal.CancelInvoke(signal.KillMessage);
 signal.Kill();

 if (!HasPermission(player))
 {
 GiveUAVSignal(player);
 player.ChatMessage(GetMessage("NoUsePermission", player.UserIDString));
 return;
 }

 StartUAV(player);
 }

 private void OnEntityDeath(BaseCombatEntity entity, HitInfo info)
 {
 var player = entity as BasePlayer;
 if (player == null) return;

 if (playersWithUI.Contains(player))
 {
 HideTrackedIcon(player);
 }

 UavSession session;
 if (activeSessions.TryGetValue(player.userID.Get(), out session))
 {
 DestroyUAV(session);
 }
 }

 private void OnPlayerDisconnected(BasePlayer player, string reason)
 {
 if (player == null) return;

 if (playersWithUI.Contains(player))
 {
 HideTrackedIcon(player);
 }

 UavSession session;
 if (activeSessions.TryGetValue(player.userID.Get(), out session))
 {
 DestroyUAV(session);
 }

 openAdminPanels.Remove(player.userID.Get());
 }

 private object CanLootEntity(BasePlayer player, LootContainer container)
 {
 if (container == null || config == null || !config.Loot.Enabled)
 return null;

 if (!processedContainers.Add(container))
 return null;

 string containerName = container.ShortPrefabName;
 float dropChance;
 if (!config.Loot.Containers.TryGetValue(containerName, out dropChance))
 return null;

 if (!RollChance(dropChance))
 return null;

 if (container.inventory == null)
 return null;

 if (container.inventory.itemList.Count >= container.inventory.capacity)
 return null;

 var item = ItemManager.CreateByName(SupplySignalShortname, 1, config.UAV.SkinID);
 if (item != null)
 {
 item.name = config.UAV.ItemName;
 if (!item.MoveToContainer(container.inventory))
 {
 item.Remove();
 }
 }

 return null;
 }

 private void OnEntityKill(LootContainer container)
 {
 if (container != null)
 {
 processedContainers.Remove(container);
 }
 }

 #endregion

 #region UAV Logic

 private void StartUAV(BasePlayer player)
 {
 if (player == null) return;

 ulong id = player.userID.Get();
 if (activeSessions.ContainsKey(id))
 {
 player.ChatMessage(GetMessage("UAVAlreadyActive", player.UserIDString));
 GiveUAVSignal(player);
 return;
 }

 var session = new UavSession { Caller = player };
 activeSessions[id] = session;

 SpawnJet(session);
 player.ChatMessage(GetMessage("UAVCalled", player.UserIDString));
 DebugLog(
 $"UAV started by {player.displayName} at position {player.transform.position}"
 );
 }

 private void SpawnJet(UavSession session)
 {
 var player = session.Caller;
 Vector3 targetPosition = player.transform.position;
 Vector3 spawnPosition =
 targetPosition + player.transform.forward.normalized * config.Jet.SpawnDistance;

 var jet = GameManager.server.CreateEntity(F15Prefab, spawnPosition) as F15;
 if (jet == null)
 {
 PrintError("Failed to create F15 entity.");
 activeSessions.Remove(player.userID.Get());
 return;
 }

 jet.transform.position = spawnPosition;
 jet.transform.forward = (targetPosition - spawnPosition).normalized;
 if (F15MovePositionField != null)
 {
 F15MovePositionField.SetValue(jet, targetPosition);
 }
 else
 {
 PrintWarning("F15.movePosition field not found; jet may not move to target.");
 }
 jet.defaultAltitude = config.Jet.Altitude;
 jet.Spawn();

 timer.Once(config.Jet.Duration, () =>
 {
 if (jet != null && !jet.IsDestroyed)
 jet.Kill();
 });

 ulong callerId = player.userID.Get();
 timer.Once(config.UAV.Warmup, () =>
 {
 if (activeSessions.ContainsKey(callerId))
 ActivateUAV(session);
 });
 DebugLog("Jet spawned and moving to target position");
 }

 private void ActivateUAV(UavSession session)
 {
 var player = session.Caller;
 if (player == null || !player.IsConnected)
 {
 DestroyUAV(session);
 return;
 }

 player.ChatMessage(GetMessage("UAVActive", player.UserIDString));
 TickSession(session);

 session.TickTimer = timer.Every(3f, () => TickSession(session));
 timer.Once(config.UAV.Duration, () => DestroyUAV(session));
 DebugLog("UAV activated and tick timer started");
 }

 private void TickSession(UavSession session)
 {
 var caller = session.Caller;
 if (caller == null || !caller.IsConnected)
 {
 DestroyUAV(session);
 return;
 }

 UpdatePlayersInRadius(session);
 MarkPlayers(session);
 }

 private void UpdatePlayersInRadius(UavSession session)
 {
 var caller = session.Caller;

 var scratch = Pool.Get<List<BasePlayer>>();
 Vis.Entities<BasePlayer>(
 caller.transform.position,
 config.UAV.Radius,
 scratch,
 Rust.Layers.Mask.Player_Server
 );

 var previous = Pool.Get<List<BasePlayer>>();
 foreach (var p in session.PlayersInRadius)
 {
 previous.Add(p);
 }
 session.PlayersInRadius.Clear();

 for (int i = 0; i < scratch.Count; i++)
 {
 var p = scratch[i];
 if (p == null || p == caller) continue;
 if (p.userID.Get().IsSteamId() && !p.IsConnected) continue;

 var category = ClassifyTarget(caller, p);
 var setting = GetCategorySetting(category);

 if (!setting.Enabled)
 {
 if (session.LoggedSkips.Add(p.userID.Get()))
 {
 DebugLog(
 $"Skipped {category} '{(p.IsNpc ? p.ShortPrefabName : p.displayName)}' ({category} tracking disabled)"
 );
 }
 continue;
 }

 session.PlayersInRadius.Add(p);

 if (session.LoggedDetections.Add(p.userID.Get()))
 {
 if (p.IsNpc)
 {
 DebugLog(
 $"UAV has detected NPC '{p.ShortPrefabName}' at position {p.transform.position}"
 );
 }
 else
 {
 DebugLog(
 $"UAV has detected player '{p.displayName}' (ID: {p.UserIDString}) at position {p.transform.position}"
 );
 }
 }

 if (!playersWithUI.Contains(p))
 {
 ShowTrackedIcon(p);
 }
 }

 for (int i = 0; i < previous.Count; i++)
 {
 var p = previous[i];
 if (p != null && !session.PlayersInRadius.Contains(p))
 {
 DebugLog(
 $"Stopped tracking {(p.IsNpc ? "NPC" : "Player")} '{p.displayName}'"
 );
 }
 }

 foreach (var p in playersWithUI.ToList())
 {
 if (!IsTrackedByAnySession(p))
 {
 HideTrackedIcon(p);
 }
 }

 Pool.FreeUnmanaged(ref scratch);
 Pool.FreeUnmanaged(ref previous);
 }

 private bool IsTrackedByAnySession(BasePlayer player)
 {
 foreach (var session in activeSessions.Values)
 {
 if (session.PlayersInRadius.Contains(player))
 return true;
 }
 return false;
 }

 private void MarkPlayers(UavSession session)
 {
 var player = session.Caller;
 if (player == null || !player.IsConnected)
 return;

 foreach (var target in session.PlayersInRadius)
 {
 if (target == null || target.IsDead())
 {
 DebugLog("Target is null or dead");
 continue;
 }

 var category = ClassifyTarget(player, target);
 var type = GetCategorySetting(category).ParsedPingType;

 Vector3 pingPosition = target.transform.position + Vector3.up * 2f;
 player.AddPingAtLocation(type, pingPosition, 3f, target.net.ID);
 DebugLog(
 $"Added ping for {category} '{target.displayName}' type {type} at position {pingPosition}"
 );
 }
 }

 private void DestroyUAV(UavSession session, bool isUnloading = false)
 {
 if (session == null) return;

 if (session.TickTimer != null && !session.TickTimer.Destroyed)
 {
 session.TickTimer.Destroy();
 }

 session.PlayersInRadius.Clear();
 session.LoggedDetections.Clear();
 session.LoggedSkips.Clear();

 var caller = session.Caller;
 if (caller != null)
 {
 activeSessions.Remove(caller.userID.Get());
 }

 foreach (var p in playersWithUI.ToList())
 {
 if (!IsTrackedByAnySession(p))
 {
 HideTrackedIcon(p);
 }
 }

 if (caller != null)
 {
 if (!isUnloading && caller.IsConnected)
 {
 caller.ChatMessage(GetMessage("UAVEnded", caller.UserIDString));
 }
 DebugLog($"UAV ended for {caller.displayName}");
 }
 }

 private void ShowTrackedIcon(BasePlayer player)
 {
 if (player == null || !player.IsConnected)
 return;

 if (playersWithUI.Contains(player))
 return;

 CuiElementContainer elements = new CuiElementContainer();

 elements.Add(
 new CuiPanel
 {
 Image = { Color = config.UAV.PanelColor },
 RectTransform =
 {
 AnchorMin = config.UAV.TrackedCUIAnchorMin,
 AnchorMax = config.UAV.TrackedCUIAnchorMax,
 },
 CursorEnabled = false,
 },
 "Hud",
 TrackedPanelName
 );

 elements.Add(
 new CuiLabel
 {
 Text =
 {
 Color = config.UAV.TextColor,
 FontSize = config.UAV.TextFontSize,
 Align = TextAnchor.MiddleLeft,
 Text = GetMessage("BeingTracked", player.UserIDString),
 },
 RectTransform = { AnchorMin = "0.21 0.1", AnchorMax = "0.98 0.9" },
 },
 TrackedPanelName
 );

 var iconComponent = new CuiRawImageComponent { Color = "1 1 1 1" };
 string cachedPng = null;
 if (trackedIconLoaded && ImageLibrary != null)
 {
 cachedPng = ImageLibrary.Call("GetImage", TrackedIconImageKey, 0UL, false) as string;
 }
 if (!string.IsNullOrEmpty(cachedPng))
 {
 iconComponent.Png = cachedPng;
 }
 else
 {
 iconComponent.Url = config.UAV.TrackedIconUrl;
 }

 elements.Add(
 new CuiElement
 {
 Parent = TrackedPanelName,
 Components =
 {
 iconComponent,
 new CuiRectTransformComponent { AnchorMin = "0.02 0.12", AnchorMax = "0.20 0.88" },
 },
 }
 );

 CuiHelper.AddUi(player, elements);
 playersWithUI.Add(player);
 }

 private void HideTrackedIcon(BasePlayer player)
 {
 if (player == null)
 return;

 if (!playersWithUI.Remove(player))
 return;

 if (player.IsConnected)
 {
 CuiHelper.DestroyUi(player, TrackedPanelName);
 }
 }

 #endregion

 #region Helpers

 private bool HasPermission(BasePlayer player)
 {
 return permission.UserHasPermission(player.UserIDString, PermissionUse);
 }

 private bool HasAdminPermission(BasePlayer player)
 {
 return permission.UserHasPermission(player.UserIDString, PermissionAdmin);
 }

 private BasePlayer FindPlayer(string nameOrId, out List<string> ambiguousMatches)
 {
 ambiguousMatches = null;

 foreach (var p in BasePlayer.activePlayerList)
 {
 if (p.UserIDString == nameOrId)
 return p;
 }

 foreach (var p in BasePlayer.activePlayerList)
 {
 if (string.Equals(p.displayName, nameOrId, StringComparison.OrdinalIgnoreCase))
 return p;
 }

 var partial = BasePlayer
 .activePlayerList.Where(p =>
 p.displayName.IndexOf(nameOrId, StringComparison.OrdinalIgnoreCase) >= 0
 )
 .ToList();

 if (partial.Count == 1)
 return partial[0];

 if (partial.Count > 1)
 {
 ambiguousMatches = partial.Select(p => p.displayName).ToList();
 }

 return null;
 }

 private void GiveUAVSignal(BasePlayer player)
 {
 var item = ItemManager.CreateByName(SupplySignalShortname, 1, config.UAV.SkinID);
 if (item != null)
 {
 item.name = config.UAV.ItemName;
 player.GiveItem(item);
 DebugLog($"Gave UAV signal to {player.displayName}");
 }
 }

 private bool RollChance(float chance)
 {
 return UnityEngine.Random.Range(0f, 100f) <= chance;
 }

 private TargetCategory ClassifyTarget(BasePlayer caller, BasePlayer target)
 {
 if (caller.Team != null && caller.Team.members.Contains(target.userID))
 return TargetCategory.Teammate;

 if (!target.userID.Get().IsSteamId())
 return TargetCategory.NPC;

 return TargetCategory.OtherPlayer;
 }

 private CategorySetting GetCategorySetting(TargetCategory category)
 {
 switch (category)
 {
 case TargetCategory.Teammate: return config.Tracking.Teammates;
 case TargetCategory.NPC: return config.Tracking.NPCs;
 default: return config.Tracking.OtherPlayers;
 }
 }

 private void DebugLog(string message)
 {
 if (config != null && config.DebugMode)
 {
 Puts(message);
 }
 }

 #endregion

 #region Admin Panel

 private void OpenAdminPanel(BasePlayer player, string tab)
 {
 if (player == null || !player.IsConnected) return;

 CuiHelper.DestroyUi(player, AdminPanelName);

 var container = new CuiElementContainer();

 container.Add(new CuiPanel
 {
 Image = { Color = "0.1 0.1 0.1 0.96" },
 RectTransform = { AnchorMin = "0.18 0.15", AnchorMax = "0.82 0.85" },
 CursorEnabled = true,
 }, "Overlay", AdminPanelName);

 container.Add(new CuiLabel
 {
 Text = { Text = $"UAV Signal, Admin Panel v{Version}", FontSize = 18, Align = TextAnchor.MiddleLeft, Color = "1 1 1 1" },
 RectTransform = { AnchorMin = "0.02 0.92", AnchorMax = "0.85 0.99" },
 }, AdminPanelName);

 container.Add(new CuiButton
 {
 Button = { Color = "0.7 0.2 0.2 1", Command = "uavadmin.close" },
 RectTransform = { AnchorMin = "0.95 0.93", AnchorMax = "0.99 0.99" },
 Text = { Text = "X", FontSize = 14, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1" },
 }, AdminPanelName);

 bool isInfo = tab != "plugins";

 container.Add(new CuiButton
 {
 Button = { Color = isInfo ? "0.4 0.6 0.4 1" : "0.3 0.3 0.3 1", Command = "uavadmin.tab info" },
 RectTransform = { AnchorMin = "0.02 0.84", AnchorMax = "0.25 0.91" },
 Text = { Text = "Info & Config", FontSize = 14, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1" },
 }, AdminPanelName);

 container.Add(new CuiButton
 {
 Button = { Color = !isInfo ? "0.4 0.6 0.4 1" : "0.3 0.3 0.3 1", Command = "uavadmin.tab plugins" },
 RectTransform = { AnchorMin = "0.26 0.84", AnchorMax = "0.49 0.91" },
 Text = { Text = "More Plugins", FontSize = 14, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1" },
 }, AdminPanelName);

 container.Add(new CuiPanel
 {
 Image = { Color = "0.05 0.05 0.05 0.92" },
 RectTransform = { AnchorMin = "0.02 0.02", AnchorMax = "0.98 0.83" },
 }, AdminPanelName, AdminContentName);

 if (isInfo)
 AddInfoTabContent(container);
 else
 AddPluginsTabContent(container);

 CuiHelper.AddUi(player, container);
 openAdminPanels.Add(player.userID.Get());
 }

 private void AddInfoTabContent(CuiElementContainer container)
 {
 int trackedCount = 0;
 foreach (var s in activeSessions.Values)
 trackedCount += s.PlayersInRadius.Count;

 string info =
 $"Active UAV sessions: {activeSessions.Count}\n" +
 $"Players currently tracked: {trackedCount}\n" +
 $"Tracked icons shown: {playersWithUI.Count}\n" +
 $"Loot drops: {(config.Loot.Enabled ? "ON" : "OFF")}\n" +
 $"Debug mode: {(config.DebugMode ? "ON" : "OFF")}";

 container.Add(new CuiLabel
 {
 Text = { Text = "<b>Status</b>", FontSize = 14, Align = TextAnchor.UpperLeft, Color = "1 1 1 1" },
 RectTransform = { AnchorMin = "0.02 0.92", AnchorMax = "0.5 0.99" },
 }, AdminContentName);

 container.Add(new CuiLabel
 {
 Text = { Text = info, FontSize = 13, Align = TextAnchor.UpperLeft, Color = "0.9 0.9 0.9 1" },
 RectTransform = { AnchorMin = "0.02 0.74", AnchorMax = "0.5 0.91" },
 }, AdminContentName);

 container.Add(new CuiLabel
 {
 Text = { Text = "<b>Settings (read-only)</b>", FontSize = 13, Align = TextAnchor.UpperLeft, Color = "1 1 1 1" },
 RectTransform = { AnchorMin = "0.02 0.66", AnchorMax = "0.5 0.73" },
 }, AdminContentName);

 string nums =
 $"UAV Duration: {config.UAV.Duration}s\n" +
 $"UAV Radius: {config.UAV.Radius}m\n" +
 $"UAV Warmup: {config.UAV.Warmup}s\n" +
 $"Jet Altitude: {config.Jet.Altitude}m\n" +
 $"Jet Duration: {config.Jet.Duration}s";

 container.Add(new CuiLabel
 {
 Text = { Text = nums, FontSize = 12, Align = TextAnchor.UpperLeft, Color = "0.7 0.7 0.7 1" },
 RectTransform = { AnchorMin = "0.02 0.46", AnchorMax = "0.5 0.65" },
 }, AdminContentName);

 container.Add(new CuiLabel
 {
 Text = { Text = "<b>Active Skin</b>", FontSize = 13, Align = TextAnchor.UpperLeft, Color = "1 1 1 1" },
 RectTransform = { AnchorMin = "0.02 0.40", AnchorMax = "0.5 0.45" },
 }, AdminContentName);

 container.Add(new CuiElement
 {
 Parent = AdminContentName,
 Components =
 {
 new CuiImageComponent
 {
 ItemId = supplySignalItemId,
 SkinId = config.UAV.SkinID,
 },
 new CuiRectTransformComponent { AnchorMin = "0.02 0.02", AnchorMax = "0.22 0.40" },
 },
 });

 container.Add(new CuiLabel
 {
 Text = { Text = $"Skin ID:\n{config.UAV.SkinID}", FontSize = 13, Align = TextAnchor.MiddleLeft, Color = "0.85 0.85 0.85 1" },
 RectTransform = { AnchorMin = "0.24 0.02", AnchorMax = "0.5 0.40" },
 }, AdminContentName);

 container.Add(new CuiLabel
 {
 Text = { Text = "<b>Toggles</b>", FontSize = 14, Align = TextAnchor.UpperLeft, Color = "1 1 1 1" },
 RectTransform = { AnchorMin = "0.55 0.88", AnchorMax = "0.98 0.97" },
 }, AdminContentName);

 AddToggleRow(container, 0, "Debug Mode", config.DebugMode, "uavadmin.toggle debug");
 AddToggleRow(container, 1, "Loot Drops", config.Loot.Enabled, "uavadmin.toggle loot");
 AddToggleRow(container, 2, "Track Teammates", config.Tracking.Teammates.Enabled, "uavadmin.toggle teammates");
 AddToggleRow(container, 3, "Track NPCs", config.Tracking.NPCs.Enabled, "uavadmin.toggle npcs");
 AddToggleRow(container, 4, "Track Other Players", config.Tracking.OtherPlayers.Enabled, "uavadmin.toggle otherplayers");

 container.Add(new CuiButton
 {
 Button = { Color = "0.3 0.5 0.7 1", Command = "uavadmin.reload" },
 RectTransform = { AnchorMin = "0.55 0.05", AnchorMax = "0.98 0.14" },
 Text = { Text = "Reload Config From Disk", FontSize = 14, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1" },
 }, AdminContentName);
 }

 private void AddToggleRow(CuiElementContainer container, int row, string label, bool state, string command)
 {
 float top = 0.86f - row * 0.12f;
 float bottom = top - 0.10f;

 container.Add(new CuiLabel
 {
 Text = { Text = label, FontSize = 13, Align = TextAnchor.MiddleLeft, Color = "0.9 0.9 0.9 1" },
 RectTransform =
 {
 AnchorMin = $"0.55 {bottom.ToString("0.000", System.Globalization.CultureInfo.InvariantCulture)}",
 AnchorMax = $"0.85 {top.ToString("0.000", System.Globalization.CultureInfo.InvariantCulture)}",
 },
 }, AdminContentName);

 container.Add(new CuiButton
 {
 Button = { Color = state ? "0.3 0.6 0.3 1" : "0.6 0.3 0.3 1", Command = command },
 RectTransform =
 {
 AnchorMin = $"0.86 {bottom.ToString("0.000", System.Globalization.CultureInfo.InvariantCulture)}",
 AnchorMax = $"0.98 {top.ToString("0.000", System.Globalization.CultureInfo.InvariantCulture)}",
 },
 Text = { Text = state ? "ON" : "OFF", FontSize = 12, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1" },
 }, AdminContentName);
 }

 private void AddPluginsTabContent(CuiElementContainer container)
 {
 container.Add(new CuiLabel
 {
 Text = { Text = "More plugins by Yac Vaguer", FontSize = 16, Align = TextAnchor.MiddleLeft, Color = "1 1 1 1" },
 RectTransform = { AnchorMin = "0.02 0.9", AnchorMax = "0.7 0.98" },
 }, AdminContentName);

 container.Add(new CuiButton
 {
 Button = { Color = "0.3 0.5 0.7 1", Command = "uavadmin.profile" },
 RectTransform = { AnchorMin = "0.72 0.91", AnchorMax = "0.98 0.98" },
 Text = { Text = "Open Codefling profile", FontSize = 12, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1" },
 }, AdminContentName);

 for (int i = 0; i < OtherPlugins.Length; i++)
 {
 var p = OtherPlugins[i];
 float top = 0.88f - i * 0.21f;
 float bottom = top - 0.18f;
 string rowName = AdminContentName + ".row" + i;

 container.Add(new CuiPanel
 {
 Image = { Color = "0.15 0.15 0.15 0.85" },
 RectTransform =
 {
 AnchorMin = $"0.02 {bottom.ToString("0.000", System.Globalization.CultureInfo.InvariantCulture)}",
 AnchorMax = $"0.98 {top.ToString("0.000", System.Globalization.CultureInfo.InvariantCulture)}",
 },
 }, AdminContentName, rowName);

 container.Add(new CuiLabel
 {
 Text = { Text = $"<b>{p.Name}</b>", FontSize = 14, Align = TextAnchor.UpperLeft, Color = "1 1 1 1" },
 RectTransform = { AnchorMin = "0.02 0.55", AnchorMax = "0.78 0.95" },
 }, rowName);

 container.Add(new CuiLabel
 {
 Text = { Text = p.Description, FontSize = 11, Align = TextAnchor.UpperLeft, Color = "0.75 0.75 0.75 1" },
 RectTransform = { AnchorMin = "0.02 0.05", AnchorMax = "0.78 0.55" },
 }, rowName);

 container.Add(new CuiButton
 {
 Button = { Color = "0.3 0.5 0.7 1", Command = $"uavadmin.link {i}" },
 RectTransform = { AnchorMin = "0.8 0.2", AnchorMax = "0.98 0.8" },
 Text = { Text = "Get Link", FontSize = 13, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1" },
 }, rowName);
 }
 }

 private void CloseAdminPanel(BasePlayer player)
 {
 if (player == null) return;
 CuiHelper.DestroyUi(player, AdminPanelName);
 openAdminPanels.Remove(player.userID.Get());
 }

 #endregion

 #region Configuration

 private enum TargetCategory
 {
 Teammate,
 NPC,
 OtherPlayer,
 }

 private class Configuration
 {
 [JsonProperty("UAV Settings")]
 public UAVSettings UAV { get; set; } = new UAVSettings();

 [JsonProperty("Jet Settings")]
 public JetSettings Jet { get; set; } = new JetSettings();

 [JsonProperty("Loot Settings")]
 public LootSettings Loot { get; set; } = new LootSettings();

 [JsonProperty("Tracking Settings")]
 public TrackingSettings Tracking { get; set; } = new TrackingSettings();

 [JsonProperty("Debug Mode")]
 public bool DebugMode { get; set; } = false;
 }

 private class TrackingSettings
 {
 [JsonProperty("Teammates")]
 public CategorySetting Teammates { get; set; } =
 new CategorySetting { Enabled = false, PingType = "Hostile" };

 [JsonProperty("NPCs")]
 public CategorySetting NPCs { get; set; } =
 new CategorySetting { Enabled = true, PingType = "Gun" };

 [JsonProperty("Other Players")]
 public CategorySetting OtherPlayers { get; set; } =
 new CategorySetting { Enabled = true, PingType = "Hostile" };
 }

 private class CategorySetting
 {
 [JsonProperty("Enabled")]
 public bool Enabled { get; set; }

 [JsonProperty("Ping Type")]
 public string PingType { get; set; }

 [JsonIgnore]
 public BasePlayer.PingType ParsedPingType { get; set; }
 }

 private class UAVSettings
 {
 [JsonProperty("Duration (seconds)")]
 public float Duration { get; set; } = 180f;

 [JsonProperty("Radius")]
 public float Radius { get; set; } = 40f;

 [JsonProperty("Skin ID")]
 public ulong SkinID { get; set; } = 3720628271;

 [JsonProperty("Warmup Time (seconds)")]
 public float Warmup { get; set; } = 5f;

 [JsonProperty("Item Name")]
 public string ItemName { get; set; } = "UAV Signal";

 [JsonProperty("Tracked Icon URL")]
 public string TrackedIconUrl { get; set; } =
 "https://media.yacvaguer.cc/rust/uav-signal/tracked-icon.png";

 [JsonProperty("Tracked Icon Position (AnchorMin)")]
 public string TrackedCUIAnchorMin { get; set; } = "0.006 0.485";

 [JsonProperty("Tracked Icon Position (AnchorMax)")]
 public string TrackedCUIAnchorMax { get; set; } = "0.14 0.525";

 [JsonProperty("Panel Color")]
 public string PanelColor { get; set; } = "0.18 0.20 0.22 0.65";

 [JsonProperty("Text Color")]
 public string TextColor { get; set; } = "0.92 0.94 0.96 1";

 [JsonProperty("Text Font Size")]
 public int TextFontSize { get; set; } = 12;
 }

 private class JetSettings
 {
 [JsonProperty("Altitude")]
 public float Altitude { get; set; } = 200f;

 [JsonProperty("Spawn Distance")]
 public float SpawnDistance { get; set; } = 500f;

 [JsonProperty("Duration (seconds)")]
 public float Duration { get; set; } = 15f;
 }

 private class LootSettings
 {
 [JsonProperty("Enable Loot Drops")]
 public bool Enabled { get; set; } = true;

 [JsonProperty("Loot Containers and Drop Chances")]
 public Dictionary<string, float> Containers { get; set; } =
 new Dictionary<string, float>
 {
 { "crate_normal", 0f },
 { "crate_normal_2", 0f },
 { "crate_elite", 2f },
 { "heli_crate", 5f },
 { "bradley_crate", 5f },
 };
 }

 protected override void LoadDefaultConfig()
 {
 config = new Configuration();
 SaveConfig();
 Puts("Default configuration file created.");
 }

 private void LoadConfigValues()
 {
 try
 {
 config = Config.ReadObject<Configuration>();
 if (config == null)
 {
 LoadDefaultConfig();
 }
 else
 {
 SaveConfig();
 }
 }
 catch
 {
 PrintError("Configuration file is corrupt; loading default configuration.");
 LoadDefaultConfig();
 }

 ResolveTrackingPingTypes();
 WarnIfAllTrackingDisabled();
 }

 private void ResolveTrackingPingTypes()
 {
 if (config?.Tracking == null) return;

 ResolvePingType(config.Tracking.Teammates, BasePlayer.PingType.Hostile, "Teammates");
 ResolvePingType(config.Tracking.NPCs, BasePlayer.PingType.Gun, "NPCs");
 ResolvePingType(config.Tracking.OtherPlayers, BasePlayer.PingType.Hostile, "Other Players");
 }

 private void ResolvePingType(CategorySetting category, BasePlayer.PingType fallback, string label)
 {
 if (category == null) return;

 BasePlayer.PingType parsed;
 if (Enum.TryParse(category.PingType, true, out parsed) && Enum.IsDefined(typeof(BasePlayer.PingType), parsed))
 {
 category.ParsedPingType = parsed;
 return;
 }

 PrintWarning(
 $"Invalid Ping Type '{category.PingType}' for {label}. Falling back to {fallback}. Valid values: {string.Join(", ", Enum.GetNames(typeof(BasePlayer.PingType)))}."
 );
 category.ParsedPingType = fallback;
 }

 private void WarnIfAllTrackingDisabled()
 {
 if (config?.Tracking == null) return;

 if (!config.Tracking.Teammates.Enabled
 && !config.Tracking.NPCs.Enabled
 && !config.Tracking.OtherPlayers.Enabled)
 {
 PrintWarning("All tracking categories disabled, UAV will detect nothing.");
 }
 }

 protected override void SaveConfig()
 {
 Config.WriteObject(config, true);
 }

 #endregion

 #region Localization

 protected override void LoadDefaultMessages()
 {
 lang.RegisterMessages(
 new Dictionary<string, string>
 {
 ["NoPermission"] = "You do not have permission to use this command.",
 ["PlayerNotFound"] = "Player '{0}' not found.",
 ["AmbiguousPlayer"] = "Multiple players match that name: {0}",
 ["UAVGiven"] = "UAV Signal given to {0}.",
 ["UAVReceived"] = "You have received a UAV Signal.",
 ["UAVCalled"] = "UAV has been called to your area.",
 ["UAVAlreadyActive"] = "A UAV is already active.",
 ["UAVActive"] = "UAV is now active.",
 ["UAVEnded"] = "UAV has ended.",
 ["BeingTracked"] = "You are being tracked",
 ["NoUsePermission"] = "You do not have permission to throw the UAV Signal.",
 ["AdminConfigReloaded"] = "UAV configuration reloaded from disk.",
 ["AdminPluginLink"] = "{0}: {1}",
 ["AdminProfileLink"] = "Yac Vaguer on Codefling: {0}",
 },
 this
 );
 }

 private string GetMessage(string key, string userId = null)
 {
 return lang.GetMessage(key, this, userId);
 }

 #endregion
 }
}

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху