using System; using System.Collections.Generic; using System.Reflection; using JetBrains.Annotations; using Newtonsoft.Json; using Oxide.Core.Plugins; using ProtoBuf; using UnityEngine; namespace Oxide.Plugins; [Info("Ping Location", "Ilovepatatos", "1.0.4")] [Description("Pings location for teammates using the middle mouse button call of duty style.")] public class PingLocation : RustPlugin { private static readonly MethodInfo s_applyPingStyle = typeof(BasePlayer).GetMethod("ApplyPingStyle", BindingFlags.Instance | BindingFlags.NonPublic); private Configuration _internalConfigs; #region Configs [Serializable] private class Configuration { [JsonProperty("Use perms")] public string UsePerms = "pinglocation.use"; [JsonProperty("Ping duration")] public float Duration = 2f; [JsonProperty("Max ping distance")] public float MaxDistance = 300; } protected override void LoadConfig() { base.LoadConfig(); try { _internalConfigs = Config.ReadObject(); SaveConfig(); } catch (Exception ex) { LoadDefaultConfig(); Puts($"The configuration file seems to be corrupted! Loading default configuration..."); Puts(ex.Message); } } protected override void LoadDefaultConfig() { _internalConfigs = new Configuration(); } protected override void SaveConfig() { Config.WriteObject(_internalConfigs); } #endregion #region Hooks [UsedImplicitly] [HookMethod(nameof(Init))] private void Init() { permission.RegisterPermission(_internalConfigs.UsePerms, this); } [UsedImplicitly] [HookMethod(nameof(OnPlayerInput))] private void OnPlayerInput(BasePlayer player, InputState input) { if (input.WasJustPressed(BUTTON.FIRE_THIRD)) TryPing(player); } #endregion #region Methods private void TryPing(BasePlayer player) { Configuration configs = _internalConfigs; if (!permission.UserHasPermission(player.UserIDString, configs.UsePerms)) return; const int layer = (1 << (int)Rust.Layer.Player_Server) | (1 << (int)Rust.Layer.World) | (1 << (int)Rust.Layer.Construction) | (1 << (int)Rust.Layer.Terrain) | (1 << (int)Rust.Layer.Tree); if (!Cast(player, out Vector3 pos, configs.MaxDistance, layer)) return; player.State.pings ??= new List(); List pings = player.State.pings; if (pings.Count >= ConVar.Server.maximumPings) pings.RemoveAt(0); var note = Facepunch.Pool.Get(); note.isPing = true; note.worldPosition = pos; note.timeRemaining = configs.Duration; note.totalDuration = configs.Duration; s_applyPingStyle?.Invoke(player, new object[] { note, BasePlayer.PingType.Hostile }); pings.Add(note); player.DirtyPlayerState(); player.SendPingsToClient(); player.TeamUpdate(true); } #endregion #region Utility private static bool Cast(BasePlayer player, out Vector3 pos, float distance, int layerMask) { bool result = Physics.Raycast(player.eyes.HeadRay(), out RaycastHit hit, distance, layerMask); pos = hit.point; return result; } #endregion }