C#:
using Newtonsoft.Json;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Rust;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using UnityEngine;
using Formatter = Oxide.Core.Libraries.Covalence.Formatter;
namespace Oxide.Plugins {
[Info("TeamGuard", "https://topplugin.ru/", "2.1.3")]
[Description("Plugin allow admins to easier control player groups size.")]
/*
https://topplugin.ru/
*/
internal class TeamGuard : RustPlugin {
#region Checker
private class TGChecker : TriggerBase {
private readonly HashSet<BasePlayer> _players = new HashSet<BasePlayer>();
private SphereCollider _collider = new SphereCollider();
private Info _info;
private bool _mainCuiCreated;
private BasePlayer _player;
private ChatMessage Message => _info.ShouldShock ? new ChatMessage("TextWhileDmg", _config.RadiusCheck.MaxPlayers, _config.RadiusCheck.DamagePerTime, _config.RadiusCheck.DamageFrequencyString) : new ChatMessage("TextBeforeDmg", _config.RadiusCheck.MaxPlayers, _info.TimeBeforeShock);
private void Awake() {
_collider = GetComponent<SphereCollider>();
_collider.radius = _config.RadiusCheck.Radius;
_collider.isTrigger = true;
interestLayers = 131072;
// 17: Player (Server)
if (_config.RadiusCheck.DamagePerTime > 0) InvokeRepeating(DamageSequence, _config.RadiusCheck.DamageFrequency);
if (_config.RadiusCheck.UseChat) InvokeRepeating(ChatSequence, _config.RadiusCheck.ChatUpdateFrequency);
if (_config.RadiusCheck.UseCui) InvokeRepeating(CuiSequence, _config.RadiusCheck.CuiUpdateFrequency);
}
private TGChecker SetPlayer(BasePlayer player) {
_player = player;
return this;
}
private void InvokeRepeating(Action action, float time) => InvokeRepeating(action, time, time);
private bool ShouldCount(BasePlayer player) {
return player.IsConnected && !InWhitelistedZone(player) && !InSafeZone(player) && !IsDuelPlayer(player) && !IsEventPlayer(player) && !HasAuth(player) && player.CanInteract() && CanSee(player, _player);
}
private IEnumerator DoShock() {
_player.Hurt(_config.RadiusCheck.DamagePerTime, DamageType.ElectricShock, _player, false);
foreach (var prefab in _config.RadiusCheck.Effects) {
EffectNetwork.Send(new Effect(prefab, _player, 0u, Vector3.zero, Vector3.forward), _player.net.connection);
yield return CoroutineEx.waitForSeconds(0.3f);
}
}
public void Kill() {
DestroyCui();
Destroy(gameObject);
}
private void FixedUpdate() {
if (!ShouldCount(_player)) {
_info.Clear();
return;
}
var playersInArea = _players.Count(ShouldCount);
if (playersInArea < _config.RadiusCheck.MaxPlayers) {
_info.Remove(Time.deltaTime);
return;
}
_info.Add(Time.deltaTime);
}
private void Update() {
if (_player == null) return;
transform.rotation = _player.transform.rotation;
transform.position = _player.transform.position;
}
private void CuiSequence() {
if (!_info.OverLimit) {
DestroyCui();
return;
}
ShowCui(Message.Read(_player.UserIDString));
}
private void ChatSequence() {
if (_info.OverLimit) Message.SendToChat(_player);
}
private void DamageSequence() {
if (_info.ShouldShock) StartCoroutine(DoShock());
}
public static TGChecker Create(BasePlayer player) {
var go = new GameObject($"TGChecker for {player.userID}", typeof(SphereCollider)) {
layer = 18
}
;
//go.transform.SetParent(player.transform, false);
return go.AddComponent<TGChecker>().SetPlayer(player);
}
private struct Info {
public float Time;
public bool OverLimit;
public void Add(float deltaTime) {
Time += deltaTime;
OverLimit = true;
}
public void Remove(float deltaTime) {
Time = Mathf.MoveTowards(Time, 0, deltaTime);
OverLimit = false;
}
public void Clear() {
if (Time <= 0) return;
Time = 0;
OverLimit = false;
}
public bool ShouldShock => OverLimit && Time >= _config.RadiusCheck.DelayBeforeShock;
public string TimeBeforeShock => (_config.RadiusCheck.DelayBeforeShock - Time).ToString("0");
}
#region Cui
private void ShowCui(string text) {
if (!_mainCuiCreated) {
_config.Cui.AddMain(_player);
_mainCuiCreated = true;
}
_config.Cui.UpdateText(_player, text);
}
private void DestroyCui() {
if (!_mainCuiCreated) return;
_config.Cui.Destroy(_player);
_mainCuiCreated = false;
}
#endregion
#region Trigger
public override GameObject InterestedInObject(GameObject obj) {
obj = base.InterestedInObject(obj);
if (obj == null) return null;
var baseEntity = obj.ToBaseEntity();
if (baseEntity == null) return null;
if (baseEntity.isClient) return null;
var basePlayer = baseEntity as BasePlayer;
if (!basePlayer || basePlayer.IsDead() || basePlayer == _player || IsNpc(basePlayer)) return null;
return baseEntity.gameObject;
}
public override void OnEntityEnter(BaseEntity ent) {
var basePlayer = ent as BasePlayer;
if (!basePlayer) return;
_players.Add(basePlayer);
}
public override void OnEntityLeave(BaseEntity ent) {
var basePlayer = ent as BasePlayer;
if (!basePlayer) return;
_players.Remove(basePlayer);
}
#endregion
}
#endregion
#region Vars
private static PluginConfig _config;
private static TeamGuard _inst;
[PluginReference]
Plugin Duel, EventManager, ZoneManager;
private readonly Dictionary<BasePlayer, uint> _lastUsed = new Dictionary<BasePlayer, uint>();
private readonly Dictionary<BasePlayer, TGChecker> _checkers = new Dictionary<BasePlayer, TGChecker>();
#endregion
#region Localization
private class ChatMessage {
public static readonly ChatMessage CodeLockName = new ChatMessage("CodeLock");
public static readonly ChatMessage CupboardName = new ChatMessage("Cupboard");
public static readonly ChatMessage TurretName = new ChatMessage("Turret");
public static readonly ChatMessage ClearMode = new ChatMessage("Clear");
public static readonly ChatMessage ReplaceMode = new ChatMessage("Replace");
private readonly object[] _args;
private readonly string _langKey;
public ChatMessage(string langKey) {
_langKey = langKey;
_args = new object[0];
}
public ChatMessage(string langKey, params object[] args) {
_langKey = langKey;
_args = args;
}
public string Read(string userId = null) {
var reply = 5125;
var format = GetMsg(_langKey, userId);
if (_args.Length == 0) return format;
var args = new List<object>(_args.Length);
foreach (var arg in _args) {
var message = arg as ChatMessage;
if (message == null) {
args.Add(arg);
continue;
}
args.Add(message.Read(userId));
}
return string.Format(format, args.ToArray());
}
public void SendToChat(BasePlayer player) {
player.ChatMessage(string.Format(_config.Generic.ChatFormat, Read(player.UserIDString)));
}
public void SendToConsole(BasePlayer player) {
player.ConsoleMessage(string.Format(_config.Generic.ChatFormat, Read(player.UserIDString)));
}
}
private static string GetMsg(string langKey, string userid = null) {
return _inst.lang.GetMessage(langKey, _inst, userid);
}
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
["TextBeforeDmg"]
= "You have more players around then allowed (Allowed - {0}).\nYou have {1} seconds before you will start to get damage.", ["TextWhileDmg"] = "You have more players around then allowed (Allowed - {0}).\nAll players in the area would get {1} damage every {2} seconds " + "until redundant player will leave the area.", ["LogLimit"] = "Player \"{0}\" attempted to auth in the {1} of the player \"{2}\"\nPlayer position: {3} | Target position: {4}", ["LogCleared"] = "Player \"{0}\" has authorized and cleared auth list of {1} of the player \"{2}\"\nPlayer position: {3} | Target position: {4}", ["LogReplaced"] = "Player \"{0}\" has replaced player \"{1}\" in the {2} of the player \"{3}\"\nPlayer position: {4} | Target position: {5}", ["Replace"] = "replace first authorized player", ["Clear"] = "authorize and clear previously authorized players list", ["ChatLimit"] = "This {0} has it's auth limit.(Max - {1}).\nRetry to {2}.", ["CodeLock"] = "code lock", ["Cupboard"] = "cupboard", ["Turret"] = "turret"
}
, this);
lang.RegisterMessages(new Dictionary<string, string> {
["TextBeforeDmg"]
= "В зоне вокруг вас находится больше игроков, чем разрешено (Разрешено {0}).\nЧерез {1}с.все игроки в зоне начнут получать урон.", ["TextWhileDmg"] = "В зоне вокруг вас находится больше игроков, чем разрешено (Разрешено {0}).\nВсем игрокам в зоне будет наносится урон " + "в размере {1} каждые {2}с.до тех пор, пока лишние игроки не покинут зону проверки.", ["LogLimit"] = "Игрок \"{0}\" попытался авторизоваться в {1} игрока \"{2}\"\nПозиция игрока: {3} | Позиция цели: {4}", ["LogCleared"] = "Игрок \"{0}\" очистил список и авторизовался в {1} игрока \"{2}\"\nПозиция игрока: {3} | Позиция цели: {4}", ["LogReplaced"] = "Игрок \"{0}\" заменил игрока \"{1}\" в списке авторизованных в {2} игрока \"{3}\"\nПозиция игрока: {4} | Позиция цели: {5}", ["Replace"] = "заменить первого авторизованно игрока", ["Clear"] = "авторизоваться и очистить список ранее авторизованных игроков", ["ChatLimit"] = "Достигнут лимит авторизованных в {0}.(Максимально - {1})\nПовторите попытку для того, чтобы {2}.", ["CodeLock"] = "замке", ["Cupboard"] = "шкафу", ["Turret"] = "турели"
}
, this, "ru");
}
#endregion
#region Config
private class RadiusCheckConfig {
[JsonProperty("Частота сообщений в чате")]
public float ChatUpdateFrequency;
[JsonProperty("Частота обновления UI")]
public float CuiUpdateFrequency;
[JsonProperty("Частота нанесения урона")]
public float DamageFrequency;
[JsonProperty("Наносимый урон за раз")]
public float DamagePerTime;
[JsonProperty("Разрешённое время нахождения рядом")]
public float DelayBeforeShock;
[JsonProperty("Отключить проверку по радиусу в безопасных зонах")]
public bool DisableInSaveZone;
[JsonProperty("Список эффектов, запускающихся при получении урона")]
public List<string> Effects;
[JsonProperty("Максимальное количество игроков в радиусе")]
public int MaxPlayers;
[JsonProperty("Радиус зоны проверки")]
public float Radius;
[JsonProperty("Использовать ли проверку по радиусу")]
public bool Use;
[JsonProperty("Выводить ли сообщения о нанесении урона в чат?")]
public bool UseChat;
[JsonProperty("Использовать ли графическую панель?")]
public bool UseCui;
[JsonIgnore]
public string DamageFrequencyString;
#region Default Config
public static RadiusCheckConfig DefaultConfig => new RadiusCheckConfig {
Use = true, DisableInSaveZone = true, MaxPlayers = 4, Radius = 10, DelayBeforeShock = 20, DamageFrequency = 5, DamagePerTime = 5, UseCui = true, CuiUpdateFrequency = 1, UseChat = false, ChatUpdateFrequency = 5, Effects = new List<string> {
"assets/prefabs/npc/autoturret/effects/targetacquired.prefab", "assets/prefabs/weapons/hatchet/effects/strike_screenshake.prefab"
}
}
;
#endregion
[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context) {
DamageFrequencyString = DamageFrequency.ToString("0");
}
}
private class GenericConfig {
[JsonProperty("Формат сообщений в чате")]
private string _chatFormat;
[JsonIgnore]
public string ChatFormat;
[JsonProperty("Привилегия для просмотра логов")]
public string LogPermission;
[JsonProperty("Выводить лог в чат (false - в консоль)")]
public bool LogToChat;
#region Default Config
// ... [Code preview truncated due to length. Full source in attached file] ...