using System.Drawing; using CommandSystem.Commands.RemoteAdmin.Inventory; using Interactables.Interobjects.DoorUtils; using InventorySystem.Items; using LabApi.Events.Arguments.PlayerEvents; using LabApi.Events.Arguments.ServerEvents; using LabApi.Events.Handlers; using LabApi.Features; using LabApi.Features.Console; using LabApi.Loader.Features.Plugins; using PlayerRoles; using LabApi.Features.Wrappers; using MapGeneration; using Vector3 = UnityEngine.Vector3; using MEC; namespace CustomClasses; public class CustomClasses : Plugin { public override string Name => "CustomClasses"; public override string Author => "Code002Lover"; public override Version Version { get; } = new(1, 0, 0); public override string Description => "Adds custom classes to the game"; public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion); public JanitorConfig JanitorConfig { get; set; } = new(); public ResearchSubjectConfig ResearchSubjectConfig { get; set; } = new(); public HeadGuardConfig HeadGuardConfig { get; set; } = new(); private readonly CustomClassManager _classManager = new(); public override void Enable() { PlayerEvents.Spawned += OnPlayerSpawned; ServerEvents.RoundEnded += OnRoundEnded; } public override void Disable() { PlayerEvents.Spawned -= OnPlayerSpawned; ServerEvents.RoundEnded -= OnRoundEnded; } private void OnRoundEnded(RoundEndedEventArgs ev) { _classManager.ResetSpawnStates(); } private void OnPlayerSpawned(PlayerSpawnedEventArgs ev) { if(_classManager.TryHandleSpawn(ev.Player, JanitorConfig, typeof(JanitorConfig))) return; if(_classManager.TryHandleSpawn(ev.Player, ResearchSubjectConfig, typeof(ResearchSubjectConfig))) return; if(_classManager.TryHandleSpawn(ev.Player, HeadGuardConfig, typeof(HeadGuardConfig))) return; } } public class CustomClassManager { private readonly object _lock = new(); private readonly Random _random = new(); private readonly Dictionary _spawnStates = new(); private readonly Dictionary _handlers = new(); public CustomClassManager() { // Register handlers RegisterHandler(new JanitorHandler(this)); RegisterHandler(new ResearchSubjectHandler(this)); RegisterHandler(new HeadGuardHandler(this)); } public void TeleportPlayerToAround(Player player, Vector3 position) { player.Position = position + new Vector3(0,1,0) + new Vector3((float)(_random.NextDouble() * 2), 0, (float)(_random.NextDouble() * 2)); } private void RegisterHandler(ICustomClassHandler handler) where T : CustomClassConfig { lock (_lock) { _spawnStates[typeof(T)] = new SpawnState(); } _handlers[typeof(T)] = handler; } public void ResetSpawnStates() { lock (_lock) { foreach (var key in _spawnStates.Keys.ToList()) { _spawnStates[key] = new SpawnState(); } } } public bool TryHandleSpawn(Player player, CustomClassConfig config, Type configType) { if (player.Role != config.RequiredRole) return false; if (Player.ReadyList.Count() <= config.MinPlayers) return false; lock (_lock) { var state = _spawnStates[configType]; if (state.Spawns >= config.MaxSpawns) { Logger.Debug($"Max spawns reached {configType} - {player.Nickname}"); return false; } if (_random.NextDouble() > config.ChancePerPlayer) { Logger.Debug($"Chance not met {configType} - {player.Nickname}"); return false; } state.Spawns++; Logger.Debug($"Player spawning {configType} - {player.Nickname} - {state.Spawns} / {config.MaxSpawns}"); if (_handlers.TryGetValue(configType, out var handler)) { return handler.HandleSpawn(player, config, _random); } } return false; } } public interface ICustomClassHandler { bool HandleSpawn(Player player, CustomClassConfig config, Random random); } public class JanitorHandler(CustomClassManager manager) : ICustomClassHandler { public bool HandleSpawn(Player player, CustomClassConfig config, Random random) { var scp914 = Map.Rooms.First(r => r.Name == RoomName.Lcz914); Timing.CallDelayed(0.5f, () => { manager.TeleportPlayerToAround(player, scp914.Position); foreach (var spawnItem in config.Items) { player.AddItem(spawnItem, ItemAddReason.StartingItem); Logger.Debug($"Gave player {player.Nickname} spawn item {spawnItem}"); } player.SendBroadcast("You're a Janitor!", 3); }); return true; } } public class ResearchSubjectHandler(CustomClassManager manager) : ICustomClassHandler { public bool HandleSpawn(Player player, CustomClassConfig config, Random random) { var scientist = Player.ReadyList.First(p => p.Role == RoleTypeId.Scientist); Timing.CallDelayed(0.5f, () => { manager.TeleportPlayerToAround(player, scientist.Position); foreach (var spawnItem in config.Items) { player.AddItem(spawnItem, ItemAddReason.StartingItem); Logger.Debug($"Gave player {player.Nickname} spawn item {spawnItem}"); } player.SendBroadcast("You're a Research Subject!", 3); }); return true; } } public class HeadGuardHandler(CustomClassManager manager) : ICustomClassHandler { public bool HandleSpawn(Player player, CustomClassConfig config, Random random) { Timing.CallDelayed(0.5f, () => { player.RemoveItem(ItemType.KeycardGuard); KeycardItem.CreateCustomKeycardTaskForce(player, "Head Guard Keycard", $"HG. {player.Nickname}", new KeycardLevels(1,1,2),UnityEngine.Color.blue,UnityEngine.Color.cyan, "1", 0); player.AddItem(ItemType.Adrenaline, ItemAddReason.StartingItem); player.RemoveItem(ItemType.ArmorLight); player.AddItem(ItemType.ArmorCombat, ItemAddReason.StartingItem); player.RemoveItem(ItemType.GunFSP9); var pickup = Pickup.Create(ItemType.GunCrossvec, Vector3.one); if (pickup != null) player.AddItem(pickup); player.SetAmmo(ItemType.Ammo9x19, 120); player.SendBroadcast("You're a Head Guard!", 3); }); return true; } } internal record SpawnState { public int Spawns; } public abstract class CustomClassConfig { public virtual int MinPlayers { get; set; } = 4; public virtual double ChancePerPlayer { get; set; } = 0.7; public virtual int MaxSpawns { get; set; } = 1; public virtual ItemType[] Items { get; set; } = []; public virtual RoleTypeId RequiredRole { get; set; } = RoleTypeId.ClassD; } public class ResearchSubjectConfig : CustomClassConfig; public class JanitorConfig : CustomClassConfig { public override int MinPlayers { get; set; } = 5; public override double ChancePerPlayer { get; set; } = 0.3; public override int MaxSpawns { get; set; } = 2; public override ItemType[] Items { get; set; } = [ItemType.KeycardJanitor]; } public class HeadGuardConfig : CustomClassConfig { public override int MinPlayers { get; set; } = 9; public override RoleTypeId RequiredRole { get; set; } = RoleTypeId.FacilityGuard; }