using HintServiceMeow.Core.Enum; using HintServiceMeow.Core.Models.Hints; using HintServiceMeow.Core.Utilities; using LabApi.Events.Arguments.PlayerEvents; using LabApi.Events.Handlers; using LabApi.Features; using LabApi.Features.Console; using LabApi.Features.Wrappers; using LabApi.Loader.Features.Plugins; using PlayerRoles; using Timer = System.Timers.Timer; namespace VisibleSpectators; public class Plugin : Plugin { private static Plugin _singleton; private static readonly Dictionary GetColorMap = new() { { "DEFAULT", "FFFFFF" }, { "PUMPKIN", "EE7600" }, { "ARMY_GREEN", "4B5320" }, { "MINT", "98FB98" }, { "NICKEL", "727472" }, { "CARMINE", "960018" }, { "EMERALD", "50C878" }, { "GREEN", "228B22" }, { "LIME", "BFFF00" }, { "POLICE_BLUE", "002DB3" }, { "ORANGE", "FF9966" }, { "SILVER_BLUE", "666699" }, { "BLUE_GREEN", "4DFFB8" }, { "MAGENTA", "FF0090" }, { "YELLOW", "FAFF86" }, { "TOMATO", "FF6448" }, { "DEEP_PINK", "FF1493" }, { "AQUA", "00FFFF" }, { "CYAN", "00B7EB" }, { "CRIMSON", "DC143C" }, { "LIGHT_GREEN", "32CD32" }, { "SILVER", "A0A0A0" }, { "BROWN", "944710" }, { "RED", "C50000" }, { "PINK", "FF96DE" }, { "LIGHT_RED", "FD8272" }, { "PURPLE", "8137CE" }, { "BLUE", "005EBC" }, { "TEAL", "008080" }, { "GOLD", "EFC01A" } }; private readonly Dictionary _spectatorHints = new(); private Timer _timer; public override string Name => "VisibleSpectators"; public override string Author => "Code002Lover"; public override Version Version { get; } = new(1, 0, 0); public override string Description => "See your spectators"; public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion); public int YCoordinate { get; set; } = 100; public override void Enable() { Logger.Debug("starting..."); _singleton = this; PlayerEvents.ChangedSpectator += OnSpectate; PlayerEvents.Joined += OnJoin; _timer = new Timer(1000); _timer.Elapsed += (_, _) => UpdateSpectators(); _timer.Start(); } public override void Disable() { Logger.Debug("unloading..."); _timer.Stop(); _timer.Dispose(); _timer = null; PlayerEvents.Joined -= OnJoin; PlayerEvents.ChangedSpectator -= OnSpectate; _singleton = null; } private void UpdateSpectators() { foreach (var player in GetPlayers()) UpdateSpectators(player); } private void AddPlayerHint(Player player) { var hint = new Hint { Text = $"{Config!.HeaderMessage}\n{Config!.NoSpectatorsMessage}", Alignment = HintAlignment.Right, YCoordinate = YCoordinate, Hide = true }; var playerDisplay = PlayerDisplay.Get(player); playerDisplay.AddHint(hint); _spectatorHints[player] = hint; } private static string PlayerToDisplay(Player player) { if (player == null) return ""; if (!player.IsReady) return ""; // Default color if GroupColor is null or not found in the map const string defaultColor = "FFFFFF"; try { var groupColor = player.GroupColor; if (string.IsNullOrEmpty(groupColor)) return $"{player.DisplayName}"; return GetColorMap.TryGetValue(groupColor.ToUpper(), out var color) ? $"{player.DisplayName}" : $"{player.DisplayName}"; } catch { return $"{player.DisplayName}"; } } private static bool IsNotOverwatch(Player player) { return player != null && player.Role != RoleTypeId.Overwatch; } private void UpdateSpectators(Player player) { // Safety check - if player doesn't have a hint, create one if (!_spectatorHints.ContainsKey(player)) AddPlayerHint(player); var spectators = Config!.NoSpectatorsMessage; try { spectators = string.Join("\n", player.CurrentSpectators.Where(IsNotOverwatch).Select(PlayerToDisplay)); if (player.Role == RoleTypeId.Spectator) spectators = player.CurrentlySpectating == null ? Config!.NoSpectatorsMessage : string.Join("\n", player.CurrentlySpectating?.CurrentSpectators.Where(IsNotOverwatch) .Select(PlayerToDisplay) ?? Array.Empty()); } catch (Exception e) { Logger.Error(e); } if (spectators.Length < 2) spectators = Config!.NoSpectatorsMessage; _spectatorHints[player].Text = $"{Config!.HeaderMessage}\n{spectators}"; _spectatorHints[player].Hide = player.Role is RoleTypeId.Destroyed or RoleTypeId.None; _spectatorHints[player].YCoordinate = YCoordinate + player.CurrentSpectators.Count * 10; } private static Player[] GetPlayers() { return Player.ReadyList.Where(IsNotOverwatch).ToArray(); } private static void OnSpectate(PlayerChangedSpectatorEventArgs ev) { _singleton.UpdateSpectators(ev.OldTarget); _singleton.UpdateSpectators(ev.NewTarget); _singleton.UpdateSpectators(ev.Player); } private void OnJoin(PlayerJoinedEventArgs ev) { AddPlayerHint(ev.Player); } } public class SpectatorConfig { public string HeaderMessage { get; set; } = "Spectators:"; public string NoSpectatorsMessage { get; set; } = "No spectators"; }