C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections.Generic;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Core.Libraries.Covalence;
using Oxide.Game.Rust.Cui;
using Rust;
using UnityEngine;
using Newtonsoft.Json;
namespace Oxide.Plugins
{
[Info("Security Cameras", "Yurii,Rick", "0.1.1")]
[Description("CCTV targeting system, cameras follow players, npcs & heli")]
class SecurityCameras : CovalencePlugin
{
private const string ToggleManualControlUI = "SecurityCameras.ToggleManualControlUI";
private readonly Dictionary<BasePlayer, SecurityCamera> PlayerUIStates = new Dictionary<BasePlayer, SecurityCamera>();
/// <summary>
/// Configuration options
/// </summary>
class ConfigData
{
[JsonProperty(PropertyName = "Detection Radius")]
public int detection_radius = 30;
}
static SecurityCameras instance;
private ConfigData config = new ConfigData();
#region Config Handling
/// <summary>
/// Load default config file
/// </summary>
protected override void LoadDefaultConfig()
{
config = new ConfigData();
}
/// <summary>
/// Load the config values to the config class
/// </summary>
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<ConfigData>();
if (config == null)
{
LoadDefaultConfig();
}
}
catch
{
PrintError("Your configuration file is invalid");
LoadDefaultConfig();
return;
}
SaveConfig();
}
/// <summary>
/// Save the config file
/// </summary>
protected override void SaveConfig()
{
Config.WriteObject(config);
}
#endregion
private const string UsePerm = "securitycameras.use";
private bool HasPerms(string userID, string perm) { return (string.IsNullOrEmpty(userID) || string.IsNullOrEmpty(perm)) ? false : permission.UserHasPermission(userID, perm); }
private bool HasPerms(ulong userID, string perm) { return HasPerms(userID.ToString(), perm); }
private void Init()
{
Unsubscribe(nameof(OnEntitySpawned));
permission.RegisterPermission(UsePerm, this);
}
void OnServerInitialized()
{
instance = this;
this.Reload();
Subscribe(nameof(OnEntitySpawned));
}
void OnEntitySpawned(BaseNetworkable entity)
{
this.UpdateCameraUse(entity as CCTV_RC);
}
void OnGroupPermissionGranted(string name, string perm)
{
if (perm == UsePerm)
{
this.Reload();
}
}
void OnGroupPermissionRevoked(string name, string perm)
{
if (perm == UsePerm)
{
this.Reload();
}
}
void OnUserPermissionGranted(string id, string permName)
{
if (permName == UsePerm)
{
this.Reload();
}
}
void OnUserPermissionRevoked(string id, string permName)
{
if (permName == UsePerm)
{
this.Reload();
}
}
void OnBookmarkControlEnded(ComputerStation computerStation, BasePlayer player, BaseEntity controlledEntity)
{
this.DestroyButton(player);
}
void OnBookmarkControlStarted(ComputerStation computerStation, BasePlayer player, string bookmarkName, IRemoteControllable remoteControllable)
{
var camera = (remoteControllable as BaseNetworkable).gameObject.GetComponent<SecurityCamera>();
if (camera == null) return;
this.CreateButton(player, camera);
}
void Reload()
{
foreach (BaseNetworkable entity in BaseNetworkable.serverEntities)
{
this.UpdateCameraUse(entity as CCTV_RC);
}
}
void UpdateCameraUse(CCTV_RC camera)
{
if (camera != null)
{
if (HasPerms(camera.OwnerID, UsePerm))
{
if (camera.gameObject.GetComponent<SecurityCamera>() == null)
{
camera.gameObject.AddComponent<SecurityCamera>();
}
}
else
{
SecurityCamera component = camera.gameObject.GetComponent<SecurityCamera>();
if (component != null)
{
component.DestroyCamera();
}
}
}
}
void Unload()
{
foreach (BaseNetworkable camera in BaseNetworkable.serverEntities)
{
if (camera is CCTV_RC)
{
SecurityCamera component = camera.gameObject.GetComponent<SecurityCamera>();
if (component != null)
{
component.DestroyCamera();
}
}
}
foreach (var item in PlayerUIStates)
{
this.DestroyButton(item.Key);
}
instance = null;
}
#region Commands
[Command("toggle")]
private void UICommandToggle(IPlayer player, string cmd, string[] args)
{
if (player.IsServer) return;
var basePlayer = player.Object as BasePlayer;
if (PlayerUIStates.ContainsKey(basePlayer))
{
var camera = PlayerUIStates[basePlayer];
camera.scanning = !camera.scanning;
this.DestroyButton(basePlayer);
this.CreateButton(basePlayer, camera);
}
}
#endregion
void CreateButton(BasePlayer player, SecurityCamera camera)
{
if (camera == null)
{
return;
}
var cuiElements = new CuiElementContainer
{
{
new CuiButton
{
Text = {
Text = camera.scanning ? "Scanning" : "Manual Control",
Color = "0.97 0.92 0.88 1",
Align = TextAnchor.MiddleCenter,
FadeIn = 0.25f
},
Button =
{
Color = camera.scanning ? "0.44 0.54 0.26 1" : "0.7 0.3 0 1",
Command = "toggle"
},
RectTransform =
{
AnchorMin = "1 0",
AnchorMax = "1 0",
OffsetMin = "-150 50",
OffsetMax = "-50 80"
}
},
"Overlay",
ToggleManualControlUI
}
};
CuiHelper.AddUi(player, cuiElements);
PlayerUIStates.Add(player, camera);
}
void DestroyButton(BasePlayer player)
{
CuiHelper.DestroyUi(player, ToggleManualControlUI);
PlayerUIStates.Remove(player);
}
#region SecurityCamera
internal class SecurityCamera : MonoBehaviour
{
private ulong id;
private CCTV_RC camera { get; set; } = null;
public BaseCombatEntity target = null;
public bool scanning = true;
private void Awake()
{
camera = GetComponent<CCTV_RC>();
id = camera.net.ID.Value;
gameObject.layer = (int)Layer.Reserved1;
var collider = gameObject.GetComponent<SphereCollider>();
if (collider != null)
Destroy(collider);
collider = gameObject.AddComponent<SphereCollider>();
collider.center = Vector3.zero;
collider.radius = instance.config.detection_radius;
collider.isTrigger = true;
collider.enabled = true;
ResetTarget();
}
/// <summary>
/// Reset the cameras target
/// </summary>
public void ResetTarget()
{
target = null;
camera.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
SphereCollider collider = gameObject.GetComponent<SphereCollider>();
collider.radius = instance.config.detection_radius;
}
/// <summary>
/// New entity in range
/// </summary>
/// <param name="range"></param>
private void OnTriggerEnter(Collider range)
{
BaseCombatEntity entity = range.GetComponentInParent<BaseCombatEntity>();
if (target != null || !IsValid(entity))
{
return;
}
if (ShouldTarget(entity))
{
SetTarget(entity);
}
}
/// <summary>
/// Update entities within range
/// </summary>
/// <param name="range"></param>
private void OnTriggerStay(Collider range)
{
BaseCombatEntity entity = range.GetComponentInParent<BaseCombatEntity>();
if (!IsValid(entity))
{
return;
}
if (ShouldTarget(entity))
{
SetTarget(entity);
}
}
/// <summary>
/// Entity leaving range
/// </summary>
/// <param name="range"></param>
private void OnTriggerExit(Collider range)
{
BaseCombatEntity entity = range.GetComponentInParent<BaseCombatEntity>();
if (!IsValid(entity))
{
return;
}
if (IsTargeting(entity))
{
ResetTarget();
}
}
/// <summary>
/// Check if entity is a valid target
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
private bool IsValid(BaseCombatEntity entity)
{
if (!entity)
{
return false;
}
if (entity is BasePlayer || entity is BaseHelicopter)
{
return true;
}
if (entity is NPCPlayer && !camera.isStatic)
{
return true;
}
return false;
}
/// <summary>
/// Check if camera has line of sight to entity
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public bool HasLoS(BaseCombatEntity entity)
{
if (!IsValid(entity))
{
return false;
}
Ray ray = new Ray(camera.pivotOrigin.position, entity.transform.position - camera.transform.position);
ray.origin += ray.direction / 2;
float distance = gameObject.GetComponent<SphereCollider>().radius;
var foundEntity = RaycastAll<BaseNetworkable>(ray, distance);
if (foundEntity is BaseCombatEntity)
{
if (entity == foundEntity as BaseCombatEntity)
return true;
}
return false;
}
/// <summary>
/// Check if entity should be targeted
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
private bool ShouldTarget(BaseCombatEntity entity)
{
try
{
if (!scanning)
{
return false;
}
if (!IsValid(entity))
{
return false;
}
if (!HasLoS(entity))
{
return false;
}
if (HasBuildingPrivilege(entity as BasePlayer))
{
return false;
}
if (!camera.IsPowered())
{
return false;
}
return true;
}
catch
{
return false;
}
}
/// <summary>
/// Set the cameras target
/// </summary>
/// <param name="entity"></param>
private void SetTarget(BaseCombatEntity entity)
{
if (entity == null)
return;
target = entity;
Vector3 vector3 = Vector3Ex.Direction((entity is BasePlayer)
? (entity as BasePlayer).eyes.position
: entity.transform.position, camera.yaw.transform.position);
vector3 = camera.transform.InverseTransformDirection(vector3);
Quaternion quaternion = Quaternion.LookRotation(vector3);
Vector3 v3 = BaseMountable.ConvertVector(quaternion.eulerAngles);
camera.pitchAmount = v3.x;
camera.yawAmount = v3.y;
camera.pitchAmount = Mathf.Clamp(camera.pitchAmount, camera.pitchClamp.x, camera.pitchClamp.y);
camera.yawAmount = Mathf.Clamp(camera.yawAmount, camera.yawClamp.x, camera.yawClamp.y);
Quaternion quaternion1 = Quaternion.Euler(camera.pitchAmount, 0f, 0f);
Quaternion quaternion2 = Quaternion.Euler(0f, camera.yawAmount, 0f);
camera.pitch.transform.localRotation = quaternion1;
camera.yaw.transform.localRotation = quaternion2;
camera.SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
SphereCollider collider = gameObject.GetComponent<SphereCollider>();
collider.radius = instance.config.detection_radius;
}
/// <summary>
/// Destroy collider
/// </summary>
public void DestroyCamera()
{
Destroy(this);
}
/// <summary>
/// Check if the camera is targeting an entity
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public bool IsTargeting(BaseCombatEntity entity = null)
{
if (entity != null && target != null)
if (target == entity)
return true;
if (target != null && entity == null)
return true;
return false;
}
/// <summary>
/// Check if the player has building privledge
/// </summary>
/// <param name="player"></param>
/// <returns></returns>
private bool HasBuildingPrivilege(BasePlayer player)
{
BuildingPrivlidge buildingPrivlidge = player.GetBuildingPrivilege(player.WorldSpaceBounds());
if (buildingPrivlidge && buildingPrivlidge.IsAuthed(player))
{
return true;
}
return false;
}
/// <summary>
/// Find first object in line of sight
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="ray"></param>
/// <param name="distance"></param>
/// <returns></returns>
private object RaycastAll<T>(Ray ray, float distance)
{
var hits = Physics.RaycastAll(ray, Layers.Solid);
GamePhysics.Sort(hits);
object target = false;
foreach (var hit in hits)
{
var ent = hit.GetEntity();
if (ent is T)
{
target = ent;
break;
}
}
return target;
}
}
#endregion
}
}