From 73a4da1edd3b5e5476e18557974b545ffcac1b58 Mon Sep 17 00:00:00 2001 From: code002lover Date: Fri, 6 Jun 2025 12:22:27 +0200 Subject: [PATCH] Add Template, more custom classes & fix issues --- CustomClasses/CustomClasses.cs | 169 +++++++++++++----- LobbyGame/LobbyGame.cs | 161 +++++++++++++++++ LobbyGame/LobbyGame.csproj | 57 ++++++ SCPTeamHint/SCPTeamHint.cs | 10 +- SecretPluginLaboratories.sln | 12 ++ .../.template.config/template.json | 13 ++ TemplateProject/TemplateProject.cs | 24 +++ TemplateProject/TemplateProject.csproj | 62 +++++++ VisibleSpectators/VisibleSpectators.cs | 11 +- 9 files changed, 466 insertions(+), 53 deletions(-) create mode 100644 LobbyGame/LobbyGame.cs create mode 100644 LobbyGame/LobbyGame.csproj create mode 100644 TemplateProject/.template.config/template.json create mode 100644 TemplateProject/TemplateProject.cs create mode 100644 TemplateProject/TemplateProject.csproj diff --git a/CustomClasses/CustomClasses.cs b/CustomClasses/CustomClasses.cs index 14b29fe..3423d52 100644 --- a/CustomClasses/CustomClasses.cs +++ b/CustomClasses/CustomClasses.cs @@ -1,5 +1,6 @@ using Interactables.Interobjects.DoorUtils; using InventorySystem.Items; +using InventorySystem.Items.Firearms.Modules; using LabApi.Events.Arguments.PlayerEvents; using LabApi.Events.Arguments.ServerEvents; using LabApi.Events.Handlers; @@ -28,11 +29,30 @@ public class CustomClasses : Plugin public JanitorConfig JanitorConfig { get; set; } = new(); public ResearchSubjectConfig ResearchSubjectConfig { get; set; } = new(); public HeadGuardConfig HeadGuardConfig { get; set; } = new(); + public MedicConfig MedicConfig { get; set; } = new(); + public GamblerConfig GamblerConfig { get; set; } = new(); public override void Enable() { PlayerEvents.Spawned += OnPlayerSpawned; ServerEvents.RoundEnded += OnRoundEnded; + Scp914Events.ProcessingPickup += ev => + { + if (ev.Pickup.Type < ItemType.KeycardCustomTaskForce) return; + //process custom upgrade + var keycard = (KeycardPickup)ev.Pickup; + var pickup = Pickup.Create(ItemType.KeycardMTFCaptain, keycard.Position); + keycard.Destroy(); + pickup!.Spawn(); + }; + Scp914Events.ProcessingInventoryItem += ev => + { + if (ev.Item.Type < ItemType.KeycardCustomTaskForce) return; + //process custom upgrade + var keycard = (KeycardItem)ev.Item; + ev.Player.RemoveItem(keycard); + ev.Player.AddItem(ItemType.KeycardMTFCaptain, ItemAddReason.Scp914Upgrade); + }; } public override void Disable() @@ -51,6 +71,8 @@ public class CustomClasses : Plugin 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; + if (_classManager.TryHandleSpawn(ev.Player, MedicConfig, typeof(MedicConfig))) return; + if (_classManager.TryHandleSpawn(ev.Player, GamblerConfig, typeof(GamblerConfig))) return; } } @@ -66,7 +88,9 @@ public class CustomClassManager // Register handlers RegisterHandler(new JanitorHandler(this)); RegisterHandler(new ResearchSubjectHandler(this)); - RegisterHandler(new HeadGuardHandler(this)); + RegisterHandler(new HeadGuardHandler()); + RegisterHandler(new MedicHandler()); + RegisterHandler(new GamblerHandler()); } public void TeleportPlayerToAround(Player player, Vector3 position) @@ -117,90 +141,121 @@ public class CustomClassManager Logger.Debug($"Player spawning {configType} - {player.Nickname} - {state.Spawns} / {config.MaxSpawns}"); - if (_handlers.TryGetValue(configType, out var handler)) return handler.HandleSpawn(player, config, _random); + if (!_handlers.TryGetValue(configType, out var handler)) return false; + Timing.CallDelayed(0.5f, () => { handler.HandleSpawn(player, config, _random); }); + return true; } - - return false; } } public interface ICustomClassHandler { - bool HandleSpawn(Player player, CustomClassConfig config, Random random); + void HandleSpawn(Player player, CustomClassConfig config, Random random); } public class JanitorHandler(CustomClassManager manager) : ICustomClassHandler { - public bool HandleSpawn(Player player, CustomClassConfig config, Random random) + public void 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) { - manager.TeleportPlayerToAround(player, scp914.Position); + player.AddItem(spawnItem, ItemAddReason.StartingItem); + Logger.Debug($"Gave player {player.Nickname} spawn item {spawnItem}"); + } - 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; + player.SendBroadcast("You're a Janitor!", 3); } } public class ResearchSubjectHandler(CustomClassManager manager) : ICustomClassHandler { - public bool HandleSpawn(Player player, CustomClassConfig config, Random random) + public void 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) { - manager.TeleportPlayerToAround(player, scientist.Position); + player.AddItem(spawnItem, ItemAddReason.StartingItem); + Logger.Debug($"Gave player {player.Nickname} spawn item {spawnItem}"); + } - 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; + player.SendBroadcast("You're a Research Subject!", 3); } } -public class HeadGuardHandler(CustomClassManager manager) : ICustomClassHandler +public class HeadGuardHandler : ICustomClassHandler { - public bool HandleSpawn(Player player, CustomClassConfig config, Random random) + public void 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), Color.blue, 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); + var firearm = (FirearmPickup)pickup; + if (firearm != null) { - player.RemoveItem(ItemType.KeycardGuard); + firearm.Base.Template.TryGetModule(out MagazineModule magazine); + magazine.ServerSetInstanceAmmo(firearm.Base.Template.ItemSerial, magazine.AmmoMax); + } + else + { + Logger.Error("Failed to get firearm from pickup"); + } - KeycardItem.CreateCustomKeycardTaskForce(player, "Head Guard Keycard", $"HG. {player.Nickname}", - new KeycardLevels(1, 1, 2), Color.blue, Color.cyan, "1", 0); + if (pickup != null) player.AddItem(pickup); - player.AddItem(ItemType.Adrenaline, ItemAddReason.StartingItem); + player.SetAmmo(ItemType.Ammo9x19, 120); - player.RemoveItem(ItemType.ArmorLight); - player.AddItem(ItemType.ArmorCombat, ItemAddReason.StartingItem); + player.SendBroadcast("You're a Head Guard!", 3); + } +} - player.RemoveItem(ItemType.GunFSP9); +public class MedicHandler : ICustomClassHandler +{ + public void HandleSpawn(Player player, CustomClassConfig config, Random random) + { + foreach (var spawnItem in config.Items) + { + player.AddItem(spawnItem, ItemAddReason.StartingItem); + Logger.Debug($"Gave player {player.Nickname} spawn item {spawnItem}"); + } - var pickup = Pickup.Create(ItemType.GunCrossvec, Vector3.one); + player.SendBroadcast("You're a Medic!", 3); + } +} - if (pickup != null) player.AddItem(pickup); +public abstract class SimpleAddItemHandler : ICustomClassHandler +{ + public virtual void HandleSpawn(Player player, CustomClassConfig config, Random random) + { + foreach (var spawnItem in config.Items) + { + player.AddItem(spawnItem, ItemAddReason.StartingItem); + Logger.Debug($"Gave player {player.Nickname} spawn item {spawnItem}"); + } + } +} - player.SetAmmo(ItemType.Ammo9x19, 120); - - player.SendBroadcast("You're a Head Guard!", 3); - }); - - return true; +public class GamblerHandler : SimpleAddItemHandler +{ + public override void HandleSpawn(Player player, CustomClassConfig config, Random random) + { + base.HandleSpawn(player, config, random); + player.SendBroadcast("You're a Gambler!", 3); } } @@ -233,3 +288,19 @@ public class HeadGuardConfig : CustomClassConfig public override int MinPlayers { get; set; } = 9; public override RoleTypeId RequiredRole { get; set; } = RoleTypeId.FacilityGuard; } + +public class MedicConfig : CustomClassConfig +{ + public override int MinPlayers { get; set; } = 5; + public override double ChancePerPlayer { get; set; } = 0.3; + public override int MaxSpawns { get; set; } = 1; + public override ItemType[] Items { get; set; } = [ItemType.Medkit, ItemType.Adrenaline]; + public override RoleTypeId RequiredRole { get; set; } = RoleTypeId.Scientist; +} + +public class GamblerConfig : CustomClassConfig +{ + public override double ChancePerPlayer { get; set; } = 0.3; + public override int MaxSpawns { get; set; } = 5; + public override ItemType[] Items { get; set; } = [ItemType.Coin, ItemType.Coin]; +} \ No newline at end of file diff --git a/LobbyGame/LobbyGame.cs b/LobbyGame/LobbyGame.cs new file mode 100644 index 0000000..5028ca4 --- /dev/null +++ b/LobbyGame/LobbyGame.cs @@ -0,0 +1,161 @@ +using CommandSystem.Commands.RemoteAdmin; +using GameCore; +using LabApi.Events.Arguments.ServerEvents; +using LabApi.Events.Handlers; +using LabApi.Features; +using LabApi.Loader.Features.Plugins; +using MEC; +using Interactables.Interobjects.DoorUtils; +using LabApi.Events.Arguments.PlayerEvents; +using LabApi.Features.Wrappers; +using MapGeneration; +using PlayerRoles; +using UnityEngine; +using Logger = LabApi.Features.Console.Logger; +using Version = System.Version; + +namespace LobbyGame +{ + public class LobbyGame: Plugin + { + public override string Name => "LobbyGame"; + public override string Author => "Code002Lover"; + public override Version Version { get; } = new(1, 0, 0); + public override string Description => "Adds a lobby minigame"; + public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion); + + public int RoundTimer { get; set; } = 20; + public static LobbyGame Singleton { get; private set; } + + + private bool _isStarted; + private Room _randomRoom; + + public override void Enable() + { + ServerEvents.WaitingForPlayers += WaitingForPlayers; + PlayerEvents.Joined += PlayerJoined; + Singleton = this; + } + + private static void PlayerJoined(PlayerJoinedEventArgs ev) + { + Timing.RunCoroutine(ContinuouslyTrySpawning()); + return; + + IEnumerator ContinuouslyTrySpawning() + { + while (!RoundStart.RoundStarted) + { + GameObject.Find("StartRound").transform.localScale = Vector3.zero; + yield return Timing.WaitForSeconds(0.5f); + if(Singleton._randomRoom == null) continue; + if(!Singleton._isStarted) continue; + ev.Player.SetRole(RoleTypeId.ChaosRifleman, RoleChangeReason.None, RoleSpawnFlags.None); + ev.Player.Position = Singleton._randomRoom.Position + new Vector3(0, 1, 0); + break; + } + } + } + + private static void WaitingForPlayers() + { + Timing.CallDelayed(15, () => + { + if (Singleton._isStarted) return; + + var randomRoom = Map.GetRandomRoom(FacilityZone.Entrance); + while (randomRoom is not { Zone: FacilityZone.Entrance }) + { + randomRoom = Map.GetRandomRoom(FacilityZone.Entrance); + } + Logger.Debug($"Random entrance room: {randomRoom.Name}"); + + RoundStart.LobbyLock = true; + + Singleton._randomRoom = randomRoom; + Singleton._isStarted = true; + + foreach (var player in Player.ReadyList) + { + player.SetRole(RoleTypeId.ChaosRifleman, RoleChangeReason.None, RoleSpawnFlags.None); + + try + { + player.Position = randomRoom.Position + new Vector3(0, 1, 0); + } + catch (Exception _) + { + player.SetRole(RoleTypeId.Spectator); + } + } + + Timing.RunCoroutine(ContinuouslyUpdateRoundTimer()); + + GameObject.Find("StartRound").transform.localScale = Vector3.zero; + + foreach (var door in Map.Doors) + { + door.IsOpened = false; + door.Lock(DoorLockReason.Lockdown2176, true); + } + + return; + + IEnumerator ContinuouslyUpdateRoundTimer() + { + while (true) + { + yield return Timing.WaitForSeconds(1); + + foreach (var player in Player.ReadyList) + { + player.SendBroadcast( + $"Round starts in {Singleton.RoundTimer} seconds", + 10, Broadcast.BroadcastFlags.Normal, true); + } + + Logger.Debug($"Round starts in {Singleton.RoundTimer} seconds"); + + if (Player.ReadyList.Count() <= 1) + { + Singleton.RoundTimer = 20; + continue; + } + Singleton.RoundTimer--; + if (Singleton.RoundTimer >= -1) continue; + Singleton.RoundTimer = 20; + foreach (var player in Player.ReadyList) + { + player.ClearInventory(); + player.SetRole(RoleTypeId.Spectator); + } + + foreach (var door in Map.Doors) + { + door.Lock(DoorLockReason.Lockdown2176, false); + } + + foreach (var player in Player.ReadyList) + { + player.SendBroadcast( + "", + 1, Broadcast.BroadcastFlags.Normal, true); + } + + CharacterClassManager.ForceRoundStart(); + + break; + } + } + }); + } + + public override void Disable() + { + ServerEvents.WaitingForPlayers -= WaitingForPlayers; + PlayerEvents.Joined -= PlayerJoined; + Singleton = null; + } + } +} \ No newline at end of file diff --git a/LobbyGame/LobbyGame.csproj b/LobbyGame/LobbyGame.csproj new file mode 100644 index 0000000..77f9a3e --- /dev/null +++ b/LobbyGame/LobbyGame.csproj @@ -0,0 +1,57 @@ + + + + net48 + enable + disable + latest + + + + true + true + full + + + + true + false + none + + + + + + + + + + + ..\dependencies\0Harmony.dll + + + ..\dependencies\Assembly-CSharp.dll + + + ..\dependencies\Assembly-CSharp-firstpass.dll + + + ..\dependencies\CommandSystem.Core.dll + + + ..\dependencies\HintServiceMeow-LabAPI.dll + + + ..\dependencies\Mirror.dll + + + ..\dependencies\NorthwoodLib.dll + + + ..\dependencies\Pooling.dll + + + ..\dependencies\UnityEngine.CoreModule.dll + + + diff --git a/SCPTeamHint/SCPTeamHint.cs b/SCPTeamHint/SCPTeamHint.cs index 19023a8..f4a3375 100644 --- a/SCPTeamHint/SCPTeamHint.cs +++ b/SCPTeamHint/SCPTeamHint.cs @@ -6,11 +6,11 @@ using LabApi.Events.Handlers; using LabApi.Features; using LabApi.Features.Console; using LabApi.Features.Wrappers; -using MEC; using PlayerRoles; using PlayerRoles.PlayableScps.Scp079; using PlayerRoles.PlayableScps.Scp096; using PlayerRoles.PlayableScps.Scp3114; +using Timer = System.Timers.Timer; namespace SCPTeamHint; @@ -19,6 +19,7 @@ public class Plugin : LabApi.Loader.Features.Plugins.Plugin private readonly object _hintsLock = new(); private readonly Dictionary _spectatorHints = new(); + private Timer _timer; public override string Name => "SCPTeamHint"; public override string Author => "HoherGeist, Code002Lover"; public override Version Version { get; } = new(1, 0, 0); @@ -31,13 +32,18 @@ public class Plugin : LabApi.Loader.Features.Plugins.Plugin PlayerEvents.Joined += OnJoin; PlayerEvents.Left += OnLeft; - Timing.CallContinuously(1, UpdateHints); + _timer = new Timer(1000); + _timer.Elapsed += (_, _) => UpdateHints(); + _timer.Start(); } public override void Disable() { PlayerEvents.Joined -= OnJoin; PlayerEvents.Left -= OnLeft; + _timer?.Stop(); + _timer?.Dispose(); + _timer = null; } private void UpdateHints() diff --git a/SecretPluginLaboratories.sln b/SecretPluginLaboratories.sln index eeba33d..b2b5f72 100644 --- a/SecretPluginLaboratories.sln +++ b/SecretPluginLaboratories.sln @@ -34,6 +34,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServerHints", "ServerHints\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrowingZombies", "GrowingZombies\GrowingZombies.csproj", "{5751F8D6-7A8D-4C2C-B7E9-A8A3DB324329}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemplateProject", "TemplateProject\TemplateProject.csproj", "{E5A28D1C-638F-4849-9784-240D50A6DA29}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LobbyGame", "LobbyGame\LobbyGame.csproj", "{E02243D5-0229-47BB-88A7-252EC753C8CC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -108,5 +112,13 @@ Global {5751F8D6-7A8D-4C2C-B7E9-A8A3DB324329}.Debug|Any CPU.Build.0 = Debug|Any CPU {5751F8D6-7A8D-4C2C-B7E9-A8A3DB324329}.Release|Any CPU.ActiveCfg = Release|Any CPU {5751F8D6-7A8D-4C2C-B7E9-A8A3DB324329}.Release|Any CPU.Build.0 = Release|Any CPU + {E5A28D1C-638F-4849-9784-240D50A6DA29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5A28D1C-638F-4849-9784-240D50A6DA29}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5A28D1C-638F-4849-9784-240D50A6DA29}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5A28D1C-638F-4849-9784-240D50A6DA29}.Release|Any CPU.Build.0 = Release|Any CPU + {E02243D5-0229-47BB-88A7-252EC753C8CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E02243D5-0229-47BB-88A7-252EC753C8CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E02243D5-0229-47BB-88A7-252EC753C8CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E02243D5-0229-47BB-88A7-252EC753C8CC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/TemplateProject/.template.config/template.json b/TemplateProject/.template.config/template.json new file mode 100644 index 0000000..209918b --- /dev/null +++ b/TemplateProject/.template.config/template.json @@ -0,0 +1,13 @@ +{ + "author": "Code002Lover", + "name": "SCP:SL LabAPI template", + "description": "LabAPI basic template for own use", + "identity": "Code002Lover.LabAPI.1.0", + "shortName": "labapi", + "tags": { + "language": "C#", + "type": "project" + }, + "sourceName": "TemplateProject" +} + diff --git a/TemplateProject/TemplateProject.cs b/TemplateProject/TemplateProject.cs new file mode 100644 index 0000000..1b88dce --- /dev/null +++ b/TemplateProject/TemplateProject.cs @@ -0,0 +1,24 @@ +using LabApi.Features; +using LabApi.Loader.Features.Plugins; + +namespace TemplateProject +{ + public class TemplateProject: Plugin + { + public override string Name => "TemplateProject"; + public override string Author => "Code002Lover"; + public override Version Version { get; } = new(1, 0, 0); + public override string Description => "Is a template for creating plugins. It does nothing."; + public override Version RequiredApiVersion { get; } = new(LabApiProperties.CompiledVersion); + + public override void Enable() + { + + } + + public override void Disable() + { + + } + } +} \ No newline at end of file diff --git a/TemplateProject/TemplateProject.csproj b/TemplateProject/TemplateProject.csproj new file mode 100644 index 0000000..cdb54fa --- /dev/null +++ b/TemplateProject/TemplateProject.csproj @@ -0,0 +1,62 @@ + + + + net48 + enable + disable + latest + + + + true + true + full + + + + true + false + none + + + + + + + + + + + ..\dependencies\0Harmony.dll + + + ..\dependencies\Assembly-CSharp.dll + + + ..\dependencies\Assembly-CSharp-firstpass.dll + + + ..\dependencies\CommandSystem.Core.dll + + + ..\dependencies\HintServiceMeow-LabAPI.dll + + + ..\dependencies\Mirror.dll + + + ..\dependencies\NorthwoodLib.dll + + + ..\dependencies\Pooling.dll + + + ..\dependencies\UnityEngine.CoreModule.dll + + + + + + + + diff --git a/VisibleSpectators/VisibleSpectators.cs b/VisibleSpectators/VisibleSpectators.cs index 2dbf2d4..5a77a8e 100644 --- a/VisibleSpectators/VisibleSpectators.cs +++ b/VisibleSpectators/VisibleSpectators.cs @@ -7,8 +7,8 @@ using LabApi.Features; using LabApi.Features.Console; using LabApi.Features.Wrappers; using LabApi.Loader.Features.Plugins; -using MEC; using PlayerRoles; +using Timer = System.Timers.Timer; namespace VisibleSpectators; @@ -51,6 +51,7 @@ public class Plugin : Plugin }; 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); @@ -67,13 +68,19 @@ public class Plugin : Plugin PlayerEvents.ChangedSpectator += OnSpectate; PlayerEvents.Joined += OnJoin; - Timing.CallContinuously(1, UpdateSpectators); + _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;