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 MapGeneration; using PlayerRoles; using PlayerRoles.PlayableScps.Scp079; using PlayerRoles.PlayableScps.Scp096; using PlayerRoles.PlayableScps.Scp3114; using MEC; using PlayerRoles.PlayableScps.Scp049.Zombies; namespace SCPTeamHint; public class Plugin : LabApi.Loader.Features.Plugins.Plugin { private readonly object _hintsLock = new(); private readonly Dictionary _spectatorHints = new(); public override string Name => "SCPTeamHint"; public override string Author => "HoherGeist, Code002Lover"; public override Version Version { get; } = new(1, 0, 0); public override string Description => "Displays information about your SCP Teammates"; public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion); private const string Message = "PAf4jcb1UobNURH4USLKhBQtgR/GTRD1isf6h9DvUSGmFMbdh9b/isrtgBKmGpa4HMbAhAX4gRf0Cez4h9L6UR/qh9DsUSCyCAfyhcb4gRjujBGmisQ5USD8URK0"; public override void Enable() { const string customAlphabet = "abcdefABCDEFGHIJKLMNPQRSTUghijklmnopqrstuvwxyz0123456789+/=VWXYZ"; const string standardAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; var standardized = ""; foreach (var c in Message) { var index = customAlphabet.IndexOf(c); standardized += index >= 0 ? standardAlphabet[index] : c; } // Then decode using standard base64 var decodedBytes = Convert.FromBase64String(standardized); var decodedMessage = System.Text.Encoding.UTF8.GetString(decodedBytes); Logger.Info(decodedMessage); PlayerEvents.Joined += OnJoin; PlayerEvents.Left += OnLeft; Timing.RunCoroutine(ContinuouslyUpdateHints()); } private IEnumerator ContinuouslyUpdateHints() { while (true) { yield return Timing.WaitForSeconds(1); try { UpdateHints(); } catch (Exception e) { Logger.Error(e); } } // ReSharper disable once IteratorNeverReturns } public override void Disable() { PlayerEvents.Joined -= OnJoin; PlayerEvents.Left -= OnLeft; } private static string CollectHint() { var hintTexts = new List(); foreach (var player in Player.ReadyList.Where(x => !x.IsDummy && (x.IsSCP || x.Role is RoleTypeId.Scp0492))) { var zone = player.Zone == FacilityZone.None ? Map.Elevators.Any(x=>x.Base.WorldspaceBounds.Contains(player.Position)) ? "Elevator" : "None" : player.Zone.ToString(); var text = $" {player.RoleBase.RoleName} | {(int)player.HumeShield} | {(int)player.Health} | {zone} "; switch (player.RoleBase) { case Scp096Role scp: text += "\n"; scp.SubroutineModule.TryGetSubroutine(out Scp096TargetsTracker tracker); if (!tracker) break; var targetColor = tracker.Targets.Count > 0 ? "red" : "grey"; text += $"Targets: {tracker.Targets.Count}"; break; case Scp3114Role scp3114: text += "\n"; var stolenRole = scp3114.CurIdentity.StolenRole; if (scp3114.Disguised) { text += $" {stolenRole}"; } else { text += " None"; } break; case Scp079Role scp079: text = $" {player.RoleBase.RoleName} | {scp079.CurrentCamera.Room.Zone} "; text += "\n"; scp079.SubroutineModule.TryGetSubroutine(out Scp079AuxManager auxManager); scp079.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager); if (!auxManager || !tierManager) break; text += $" AUX: {auxManager.CurrentAuxFloored} / {auxManager.MaxAux} | Level {tierManager.AccessTierLevel}"; break; case ZombieRole: if (!GrowingZombies.GrowingZombies.Instance.ZombieCorpseCount.TryGetValue(player, out var count)) break; const string corpseColor = "E68A8A"; text += "\n"; text += $" Corpses eaten: {count}"; break; } hintTexts.Add(text); } return string.Join("\n", hintTexts); } private void UpdateHints() { var hintText = CollectHint(); foreach (var player in Player.ReadyList.Where(x => !x.IsDummy)) { try { UpdateHint(player, hintText); } catch (Exception e) { Logger.Warn("Caught exception while updating hint for player"); Logger.Error(e); } } } private void UpdateHint(Player player, string hintText) { bool isContained; lock (_hintsLock) { isContained = _spectatorHints.ContainsKey(player); } if (!isContained) { CreateHint(player); } if (_spectatorHints == null) return; lock (_hintsLock) { var hint = _spectatorHints[player]; hint.Hide = player.RoleBase.Team != Team.SCPs && player.Role != RoleTypeId.Scp0492 && player.Role != RoleTypeId.Overwatch; if (!hint.Hide) hint.Text = hintText; } } private void OnJoin(PlayerJoinedEventArgs ev) { if (ev.Player.IsDummy || ev.Player.IsHost) return; CreateHint(ev.Player); } private void CreateHint(Player player) { var hint = new Hint { Text = "", Alignment = HintAlignment.Left, YCoordinate = 100, Hide = true }; var playerDisplay = PlayerDisplay.Get(player); playerDisplay.AddHint(hint); lock (_hintsLock) { _spectatorHints[player] = hint; } } private void OnLeft(PlayerLeftEventArgs ev) { if (ev.Player.IsDummy || ev.Player.IsHost) return; lock (_hintsLock) { _spectatorHints.Remove(ev.Player); } } }