using CommandSystem; using CustomPlayerEffects; using HintServiceMeow.Core.Enum; using HintServiceMeow.Core.Models.Hints; using HintServiceMeow.Core.Utilities; using Interactables.Interobjects.DoorUtils; using InventorySystem.Items.Firearms.Modules; using LabApi.Events.Handlers; using LabApi.Features.Permissions; using LabApi.Features.Wrappers; using MEC; using PlayerRoles; using UnityEngine; using Logger = LabApi.Features.Console.Logger; using Random = System.Random; namespace CustomClasses; public class SerpentsHandManager { public static bool IsSerpentsHand(Player player) => player.CustomInfo.Contains("SerpentsHand"); private readonly CustomClasses _customClasses; public SerpentsHandManager(CustomClasses customClasses) { _customClasses = customClasses; PlayerEvents.Escaping += ev => { if (!IsSerpentsHand(ev.Player)) return; var state = (SerpentsHandState) _customClasses.ClassManager.GetSpawnState(typeof(SerpentsHandConfig)); var hadItem = false; foreach (var playerItem in ev.Player.Items) { switch (playerItem.Type) { case ItemType.SCP018 or ItemType.SCP207 or ItemType.SCP244a or ItemType.SCP244b or ItemType.SCP268 or ItemType.SCP330 or ItemType.SCP500 or ItemType.SCP1344 or ItemType.SCP1576 or ItemType.SCP1853 or ItemType.SCP2176 or ItemType.AntiSCP207: state.Points += 1; hadItem = true; break; case ItemType.GunSCP127: state.Points += 2; hadItem = true; break; } } if (!hadItem) return; ev.Player.SendBroadcast("You brought back the SCP items...", 5); ev.Player.SetRole(RoleTypeId.Spectator); Logger.Info($"New SH Points: {state.Points}"); }; ServerEvents.RoundEnding += ev => { var factions = Player.ReadyList.Select(x => { return x.Team switch { Team.ChaosInsurgency or Team.ClassD => Faction.FoundationEnemy, Team.FoundationForces or Team.Scientists => Faction.FoundationStaff, Team.Flamingos => Faction.Flamingos, Team.SCPs => Faction.SCP, Team.OtherAlive when IsSerpentsHand(x) => (Faction)35, _ => Faction.Unclassified }; }).Where(x=>x!=Faction.Unclassified).GroupBy(x=>x).Select(x=>x.Key).ToArray(); if (factions.Length > 1) { ev.IsAllowed = false; return; } var state = (SerpentsHandState) _customClasses.ClassManager.GetSpawnState(typeof(SerpentsHandConfig)); if (state.Points >= 10) { ev.LeadingTeam = RoundSummary.LeadingTeam.Flamingos; } }; } public static void PreSpawn(Player player) { player.SetRole(RoleTypeId.Tutorial, RoleChangeReason.RespawnMiniwave, RoleSpawnFlags.None); const string customInfo = "SerpentsHand"; if (!Player.ValidateCustomInfo(customInfo, out var reason)) { Logger.Error($"Invalid custom info for Serpents Hand: {reason}"); } else { player.CustomInfo = customInfo; player.InfoArea |= PlayerInfoArea.CustomInfo; } } public IEnumerator UpdateSerpentsHandHint() { while (true) { yield return Timing.WaitForSeconds(1); if (_customClasses.ClassManager.GetSpawnState(typeof(SerpentsHandConfig)) is not SerpentsHandState state) continue; foreach (var player in Player.ReadyList) { if (!_customClasses.Hints.ContainsKey(player)) { _customClasses.Hints[player] = new Hint { Text = "", Alignment = HintAlignment.Center, YCoordinate = 900, Hide = true }; var playerDisplay = PlayerDisplay.Get(player); playerDisplay.AddHint(_customClasses.Hints[player]); } _customClasses.Hints[player].Text = $"Serpents Hand Chance: {state.ExtraChance + SerpentsHandConfig.BaseChance:0.00}%"; _customClasses.Hints[player].Hide = state.HasSpawned || player.Role != RoleTypeId.Spectator; } } // ReSharper disable once IteratorNeverReturns } } public sealed record SerpentsHandState: SpawnState { public bool HasSpawned => _hasSpawned || PanicDisable; public float ExtraChance; public int Points; public bool WillSpawn => _willSpawn && !PanicDisable; private bool _hasSpawned; private bool _willSpawn; public bool PanicDisable; public override void Reset() { base.Reset(); _hasSpawned = false; ExtraChance = 0f; Points = 0; _willSpawn = false; } public void SetSpawned() { _hasSpawned = true; } public void SetWillSpawn() { _willSpawn = true; } public void SetWillNotSpawn() { _willSpawn = false; } } public sealed class SerpentsHandConfig : CustomClassConfig { public override double ChancePerPlayer { get; set; } = 1.0; public override int MaxSpawns { get; set; } = int.MaxValue; public override RoleTypeId RequiredRole { get; set; } = RoleTypeId.Tutorial; public override ItemType[] Items { get; set; } = [ItemType.Painkillers, ItemType.Medkit, ItemType.ArmorCombat]; public const float BaseChance = 20f; } public class SerpentsHandHandler : SimpleAddItemHandler { public override void HandleSpawn(Player player, CustomClassConfig config, Random random) { base.HandleSpawn(player, config, random); // player.Position = new Vector3(123.921f + (float)(random.NextDouble() * 2 - 1), 288.792f, 20.929f + (float)(random.NextDouble() * 2 - 1)); player.Position = new Vector3(0.22f + (float)(random.NextDouble() * 2 - 1), 300.96f, -0.31f + (float)(random.NextDouble() * 2 - 1)); ItemType[] guns = [ItemType.GunAK, ItemType.GunE11SR, ItemType.GunCrossvec]; var gun = guns[random.Next(0, guns.Length-1)]; var gunPickup = Pickup.Create(gun, Vector3.one); if (gunPickup is FirearmPickup firearm) { if (firearm.Base.Template.TryGetModule(out MagazineModule magazine)) { magazine.ServerSetInstanceAmmo(firearm.Serial, magazine.AmmoMax); player.SetAmmo(magazine.AmmoType, 120); } else { Logger.Error("Failed to get magazine module for Serpents Hand firearm."); } } else { Logger.Error("Failed to get firearm from pickup for Serpents Hand."); } player.AddItem(gunPickup!); KeycardItem.CreateCustomKeycardTaskForce( player, "Serpent's Hand Keycard", $"SH. {player.Nickname}", new KeycardLevels(3, 3, 2), Color.black, new Color(0.271f, 0.271f, 0.271f), "SH", 3 ); player.EnableEffect(20, 30); player.SendBroadcast("You're a Serpent's Hand member!", CustomClasses.BroadcastDuration); } } [CommandHandler(typeof(RemoteAdminCommandHandler))] public class PanicDisableRemoteAdminCommand : ICommand { public string Command => "panicdisableserpentshand"; public string[] Aliases => []; public string Description => "Panic disable Serpents Hand."; public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) { var state = (SerpentsHandState)CustomClasses.Instance.ClassManager.GetSpawnState(typeof(SerpentsHandConfig)); state.PanicDisable = true; response = "Serpents Hand has been disabled."; return true; } } [CommandHandler(typeof(ClientCommandHandler))] public class PanicDisableCommand : ICommand { public string Command => "panicdisableserpentshand"; public string[] Aliases => []; public string Description => "Panic disable Serpents Hand."; public bool Execute(ArraySegment arguments, ICommandSender sender, out string response) { if (!Player.TryGet(sender, out var player)) { response = "You must be a player to use this command!"; return false; } if (!player.HasPermissions("panicdisable.serpentshand") && player.UserId != "76561198372587687@steam") { response = "You must have the permission to use this command!"; return false; } var state = (SerpentsHandState)CustomClasses.Instance.ClassManager.GetSpawnState(typeof(SerpentsHandConfig)); state.PanicDisable = true; response = "Serpents Hand has been disabled."; return true; } }