C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using ProtoBuf;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("TeamMarker", "LAGZYA", "1.0.8")]
public class TeamMarker : RustPlugin
{
#region Config
private const bool En = false;
public static TeamMarker Instance;
private ConfigData cfg { get; set; }
private class ConfigData
{
[JsonProperty(En ? "Setting pings" : "Настройка пингов")]
public PingSettings _ping = new PingSettings();
[JsonProperty(En ? "Setting sound" : "Настройка звуков")]
public SoundSettings _sound = new SoundSettings();
internal class PingSettings
{
[JsonProperty(En ? "Using player component? (E button)[High performance]" : "Использовать компонент на игроке (E button)[High performance]")]
public bool playerComponent = false;
[JsonProperty(En ? "Need aiming?" : "Нужно ли целиться?")]
public bool isNeedAiming = true;
[JsonProperty(En ? "Color index(0-yellow,1-blue,2-green,3-red,4-purple,5-cyan)" : "Цвет(0-желтый,1-синий,2-зеленый,3-красный,4-фиолетовый,5-голубой)")]
public int colorIndex = 3;
[JsonProperty(En ? "Icon type(Hostile = 0,GoTo = 1,Dollar = 2,Loot = 3,Node = 4,Gun = 5)" : "Картинка(Hostile = 0,GoTo = 1,Dollar = 2,Loot = 3,Node = 4,Gun = 5)")]
public PingType type = PingType.Hostile;
[JsonProperty(En ? "Max distance ping" : "Максимальная дистанция пинга")]
public float maxDistance = 250f;
[JsonProperty(En ? "Duration" : "Время жизни")]
public float duration = 5f;
}
internal class SoundSettings
{
[JsonProperty(En ? "Enabled?" : "Включить")]
public bool enabled = true;
[JsonProperty(En ? "Effect/sound prefab" : "Effect/sound префаб")]
public string effect = "assets/bundled/prefabs/fx/invite_notice.prefab";
[JsonProperty(En ? "Send sound teammates?" : "Отправлять звук команде?")]
public bool teamSound = true;
[JsonProperty(En ? "Only nearby teammates?" : "Отправлять звук только напарникам по близости?")]
public bool nearbyteams = true;
[JsonProperty(En ? "Max distance" : "Максимальная дистанция для отправки звука")]
public float nearbyDistance = 150f;
}
public static ConfigData GetNewConf()
{
var newConfig = new ConfigData();
return newConfig;
}
}
protected override void LoadDefaultConfig()
{
cfg = ConfigData.GetNewConf();
}
protected override void SaveConfig()
{
Config.WriteObject(cfg);
}
protected override void LoadConfig()
{
base.LoadConfig();
try
{
cfg = Config.ReadObject<ConfigData>();
}
catch
{
LoadDefaultConfig();
}
NextTick(SaveConfig);
}
#endregion
#region PlayerComponent
HashSet<BasePlayer> _components = new HashSet<BasePlayer>();
private void OnServerInitialized()
{
Instance = this;
if (cfg._ping.playerComponent)
{
foreach (var basePlayer in BasePlayer.activePlayerList)
{
OnPlayerConnected(basePlayer);
}
}
else
{
Unsubscribe(nameof(OnPlayerConnected));
Unsubscribe(nameof(OnPlayerDisconnected));
Unsubscribe(nameof(Unload));
}
}
private void OnPlayerConnected(BasePlayer player)
{
if (player == null) return;
player.gameObject.AddComponent<PlayerComponent>();
_components.Add(player);
}
private void OnPlayerDisconnected(BasePlayer player)
{
var comp = player.gameObject.GetComponent<PlayerComponent>();
UnityEngine.Object.Destroy(comp);
_components.Remove(player);
}
private void Unload()
{
foreach (var comp in _components.ToList())
{
UnityEngine.Object.Destroy(comp.GetComponent<PlayerComponent>());
}
}
class PlayerComponent : FacepunchBehaviour
{
private BasePlayer player;
private void Awake()
{
player = GetComponent<BasePlayer>();
}
private void FixedUpdate()
{
if(player == null) return;
if (player.serverInput.WasJustPressed(BUTTON.USE))
{
Instance.CreateMarker(player);
}
}
}
#endregion
[ConsoleCommand("tping")]
private void ConsolePing(ConsoleSystem.Arg arg)
{
var player = arg.Player();
CreateMarker(player);
}
private void CreateMarker(BasePlayer player)
{
if(player == null || cfg._ping.isNeedAiming && player.modelState.aiming == false) return;
if( player.eyes == null) return;
var ray = player.eyes.HeadRay();
RaycastHit hit;
if (!Physics.Raycast(ray, out hit, cfg._ping.maxDistance,
LayerMask.GetMask("Default", "Terrain", "Construction", "Water", "Deployed", "Tree", "Debris",
"World", "Player (Server)", "AI", "Ragdoll"))) return;
var entity = hit.GetEntity();
if (entity!= null && entity.ShortPrefabName.Contains("ore"))
{
CreatePing(player, hit.point, true);
return;
}
if (entity != null && entity.ShortPrefabName.Contains("player"))
{
CreatePing(player, hit.point, playr:true);
return;
}
if (entity != null && (entity.ShortPrefabName.Contains("crate") ||
entity.ShortPrefabName.Contains("loot")))
{
CreatePing(player, hit.point, loot:true);
return;
}
CreatePing(player, hit.point);
}
private void CreatePing(BasePlayer player, Vector3 pos, bool ore = false, bool playr = false, bool loot = false)
{
var activeItem = player.GetActiveItem();
if (activeItem != null)
{
if (activeItem.info.shortname == "tool.binoculars")
{
return;
}
}
PingType type;
if (playr)
{
type = PingType.Gun;
}
else if (ore)
{
type = PingType.Node;
}
else if (loot)
{
type = PingType.Loot;
}
else
{
type = PingType.Hostile;
}
if (player.State.pings == null)
{
player.State.pings = new List<MapNote>();
}
if (player.State.pings.Count >= ConVar.Server.maximumPings)
player.State.pings.RemoveAt(0);
MapNote note = Facepunch.Pool.Get<MapNote>();
note.worldPosition = pos;
note.isPing = true;
note.timeRemaining = note.totalDuration = cfg._ping.duration;
note.colourIndex = cfg._ping.colorIndex;
ApplyPingStyle(note, type);
player.State.pings.Add(note);
player.DirtyPlayerState();
player.SendPingsToClient();
player.TeamUpdate(true);
if (!cfg._sound.enabled) return;
Effect reusableSoundEffectInstance = new Effect();
if (player.Team != null )
{
var onlineMembers = player.Team.GetOnlineMemberConnections();
foreach (var onlineMemberConnection in onlineMembers)
{
if (onlineMemberConnection.userid == player.userID) continue;
var target = onlineMemberConnection.player as BasePlayer;
if (target == null) continue;
if(cfg._sound.nearbyteams)
{
reusableSoundEffectInstance.Init(Effect.Type.Generic, target,0, Vector3.zero, Vector3.forward);
reusableSoundEffectInstance.pooledString = cfg._sound.effect;
if( Vector3.Distance(player.transform.position, target.transform.position) <= cfg._sound.nearbyDistance)
EffectNetwork.Send(reusableSoundEffectInstance, target.Connection);
}
else
{
reusableSoundEffectInstance.Init(Effect.Type.Generic, target,0, Vector3.zero, Vector3.forward);
reusableSoundEffectInstance.pooledString = cfg._sound.effect;
EffectNetwork.Send(reusableSoundEffectInstance, target.Connection);
}
}
}
reusableSoundEffectInstance.Init(Effect.Type.Generic, player, 0, Vector3.zero, Vector3.forward);
reusableSoundEffectInstance.pooledString = cfg._sound.effect;
EffectNetwork.Send(reusableSoundEffectInstance, player.Connection);
}
private struct PingStyle
{
public int IconIndex;
public int ColourIndex;
public Translate.Phrase PingTitle;
public Translate.Phrase PingDescription;
public BasePlayer.PingType Type;
public PingStyle(
int icon,
int colour,
Translate.Phrase title,
Translate.Phrase desc,
BasePlayer.PingType pType)
{
this.IconIndex = icon;
this.ColourIndex = colour;
this.PingTitle = title;
this.PingDescription = desc;
this.Type = pType;
}
}
public enum PingType
{
Hostile = 0,
GoTo = 1,
Dollar = 2,
Loot = 3,
Node = 4,
Gun = 5
}
private void ApplyPingStyle(MapNote note, PingType type)
{
PingStyle pingStyle = new PingStyle();
switch (type)
{
case PingType.Hostile:
pingStyle = HostileMarker;
break;
case PingType.GoTo:
pingStyle = GoToMarker;
break;
case PingType.Dollar:
pingStyle = DollarMarker;
break;
case PingType.Loot:
pingStyle = LootMarker;
break;
case PingType.Node:
pingStyle = NodeMarker;
break;
case PingType.Gun:
pingStyle = GunMarker;
break;
}
note.icon = pingStyle.IconIndex;
}
private void ValidateMapNote(MapNote n)
{
if (n.label == null)
return;
n.label = n.label.Truncate(10).ToUpperInvariant();
}
private static readonly Translate.Phrase HostileTitle = new Translate.Phrase("ping_hostile", "Hostile");
private static readonly Translate.Phrase HostileDesc = new Translate.Phrase("ping_hostile_desc", "Danger in area");
private static readonly PingStyle HostileMarker = new PingStyle(4, 3, HostileTitle, HostileDesc, BasePlayer.PingType.Hostile);
private static readonly Translate.Phrase GoToTitle = new Translate.Phrase("ping_goto", "Go To");
private static readonly Translate.Phrase GoToDesc = new Translate.Phrase("ping_goto_desc", "Look at this");
private static readonly PingStyle GoToMarker = new PingStyle(0, 2, GoToTitle, GoToDesc, BasePlayer.PingType.GoTo);
private static readonly Translate.Phrase DollarTitle = new Translate.Phrase("ping_dollar", "Value");
private static readonly Translate.Phrase DollarDesc = new Translate.Phrase("ping_dollar_desc", "Something valuable is here");
private static readonly PingStyle DollarMarker = new PingStyle(1, 1, DollarTitle, DollarDesc, BasePlayer.PingType.Dollar);
private static readonly Translate.Phrase LootTitle = new Translate.Phrase("ping_loot", "Loot");
private static readonly Translate.Phrase LootDesc = new Translate.Phrase("ping_loot_desc", "Loot is here");
private static readonly PingStyle LootMarker = new PingStyle(11, 0, LootTitle, LootDesc, BasePlayer.PingType.Loot);
private static readonly Translate.Phrase NodeTitle = new Translate.Phrase("ping_node", "Node");
private static readonly Translate.Phrase NodeDesc = new Translate.Phrase("ping_node_desc", "An ore node is here");
private static readonly PingStyle NodeMarker = new PingStyle(10, 4, NodeTitle, NodeDesc, BasePlayer.PingType.Node);
private static readonly Translate.Phrase GunTitle = new Translate.Phrase("ping_gun", "Weapon");
private static readonly Translate.Phrase GunDesc = new Translate.Phrase("ping_weapon_desc", "A dropped weapon is here");
private static readonly PingStyle GunMarker = new PingStyle(9, 5, GunTitle, GunDesc, BasePlayer.PingType.Gun);
}
}