using Oxide.Core; using System; using System.Collections.Generic; using UnityEngine; namespace Oxide.Plugins { [Info("NpcOverlay", "Flutes", "1.0.1")] [Description("Enhances the Rust gaming environment by displaying customizable text overlays above NPCs, providing context or additional information.")] public class NpcOverlay : RustPlugin { public static NpcOverlay Instance { get; private set; } public static NpcOverlaysData Data { get; private set; } private List m_players = new List(); private void OnServerInitialized() { Instance = this; Data = NpcOverlaysData.Load(); Subscribe("OnEnterNPC"); Subscribe("OnExitNPC"); } private void Unload() { for (int i = 0; i < m_players.Count; i++) { m_players[i].Stop(); } } public void ShowOverlay(BasePlayer owner, Vector3 pos, string npcId) { string text = ""; if (Data.Overlays.ContainsKey(npcId) == false) { return; } TextData textData = Data.Overlays[npcId]; text = Lang(textData.LangKey, owner.UserIDString); pos += textData.LocalOffset; if (string.IsNullOrEmpty(text)) { Instance.Puts($"Not key {npcId} found on lang"); return; } if (!owner.IsAdmin) { owner.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, true); owner.SendNetworkUpdateImmediate(); owner.SendConsoleCommand("ddraw.text", 1f, textData.Color, pos, text); owner.SetPlayerFlag(BasePlayer.PlayerFlags.IsAdmin, false); owner.SendNetworkUpdateImmediate(); } else { owner.SendConsoleCommand("ddraw.text", 1f, textData.Color, pos, text); } } public class PlayerController { public BasePlayer Player { get; private set; } public BasePlayer Npc { get; private set; } private Timer m_timerTalk; public PlayerController(BasePlayer player, BasePlayer npc) { Player = player; Npc = npc; } public void Start() { StartRoutineTalkOverlay(); } public void Stop() { m_timerTalk.Reset(); m_timerTalk.Destroy(); } private void StartRoutineTalkOverlay() { m_timerTalk = Instance.timer.Every(1.0f, () => { if (Npc == null || Npc.IsDestroyed) return; Instance.ShowOverlay(Player, Npc.transform.position, Npc.UserIDString); }); } } private void OnEnterNPC(BasePlayer npc, BasePlayer player) { if (npc == null || player == null) return; if (Data.Overlays.ContainsKey(npc.UserIDString) == false) return; PlayerController p = m_players.Find(x => x.Player == player && x.Npc == npc); if (p == null) { p = new PlayerController(player, npc); m_players.Add(p); p.Start(); } } private void OnLeaveNPC(BasePlayer npc, BasePlayer player) { if (npc == null || player == null) return; if (Data.Overlays.ContainsKey(npc.UserIDString) == false) return; PlayerController p = m_players.Find(x => x.Player == player && x.Npc == npc); if (p != null) { m_players.Remove(p); p.Stop(); } } #region Lang public string Lang(string key, string id = null, params object[] args) { try { return string.Format(lang.GetMessage(key, this, id), args); } catch (Exception) { PrintError($"Error in the language formatting of '{key}'. (userid: {id}. lang: {lang.GetLanguage(id)}. args: {string.Join(" ,", args)})"); throw; } } #endregion #region Data public class NpcOverlaysData { public Dictionary Overlays = new Dictionary(); public static NpcOverlaysData Load() { NpcOverlaysData database = null; var file = Interface.Oxide.DataFileSystem.GetFile($"{Instance.Name}"); if (file == null) { Instance.Puts($"NpcOverlaysData Data file {Instance.Name} not found"); return null; } else { database = file.ReadObject(); } return database; } } public class TextData { public Color Color = Color.yellow; public Vector3 LocalOffset = Vector3.up; public string LangKey = ""; } #endregion } }