Compare commits

..

3 Commits

Author SHA1 Message Date
Renovate Bot
adbe516dd4 Update dependency Northwood.LabAPI to 1.1.0 2025-07-14 08:16:51 +00:00
43f66370f3 WIP refactor v3 2025-07-14 09:38:20 +02:00
ccaf44d50b refactor config handling 2025-07-14 08:11:10 +02:00
6 changed files with 93 additions and 128 deletions

View File

@ -53,45 +53,6 @@ public sealed class CustomClasses : Plugin
public const ushort BroadcastDuration = 10;
/// <summary>
/// Configuration for the Janitor class.
/// </summary>
public JanitorConfig JanitorConfig { get; private set; } = new();
/// <summary>
/// Configuration for the Research Subject class.
/// </summary>
public ResearchSubjectConfig ResearchSubjectConfig { get; private set; } = new();
/// <summary>
/// Configuration for the Head Guard class.
/// </summary>
public HeadGuardConfig HeadGuardConfig { get; private set; } = new();
/// <summary>
/// Configuration for the Medic class.
/// </summary>
public MedicConfig MedicConfig { get; private set; } = new();
/// <summary>
/// Configuration for the Gambler class.
/// </summary>
public GamblerConfig GamblerConfig { get; private set; } = new();
/// <summary>
/// Configuration for the ShadowStepper class.
/// </summary>
public ShadowStepperConfig ShadowStepperConfig { get; private set; } = new();
public MtfDemolitionistConfig MtfDemolitionistConfig { get; private set; } = new();
public ScoutConfig ScoutConfig { get; private set; } = new();
public ExplosiveMasterConfig ExplosiveMasterConfig { get; private set; } = new();
public FlashMasterConfig FlashMasterConfig { get; private set; } = new();
public SerpentsHandConfig SerpentsHandConfig { get; private set; } = new();
public NegromancerConfig NegromancerConfig { get; private set; } = new();
public NegromancerShadowConfig NegromancerShadowConfig { get; private set; } = new();
public BloodFueledConfig BloodFueledConfig { get; private set; } = new();
internal readonly Dictionary<Player, Hint> Hints = new();
public static CustomClasses Instance { get; private set; }
@ -223,9 +184,9 @@ public sealed class CustomClasses : Plugin
{
SerpentsHandManager.SpawnSerpentWave();
ClassManager.TryHandleSpawn(spectator, ScoutConfig, typeof(ScoutConfig), () =>
ClassManager.TryHandleSpawn(spectator, ClassManager.GetConfig<ScoutConfig>(), typeof(ScoutConfig), () =>
{
if (!ClassManager.ForceSpawn(spectator, SerpentsHandConfig, typeof(SerpentsHandConfig), PreSpawn))
if (!ClassManager.ForceSpawn(spectator, ClassManager.GetConfig<SerpentsHandConfig>(), typeof(SerpentsHandConfig), PreSpawn))
Logger.Error("Serpents Hand didn't spawn");
return;
@ -239,7 +200,7 @@ public sealed class CustomClasses : Plugin
}
}
if (ClassManager.TryHandleSpawn(spectator, ScoutConfig, typeof(ScoutConfig), () =>
if (ClassManager.TryHandleSpawn(spectator, ClassManager.GetConfig<ScoutConfig>(), typeof(ScoutConfig), () =>
{
spectator.SetRole(ev.Wave.Faction == Faction.FoundationStaff ? RoleTypeId.NtfPrivate : RoleTypeId.ChaosConscript, RoleChangeReason.Respawn, RoleSpawnFlags.UseSpawnpoint);
})) return;
@ -259,7 +220,7 @@ public sealed class CustomClasses : Plugin
ev.IsAllowed = false;
foreach (var evSpawningPlayer in ev.SpawningPlayers)
{
if (!ClassManager.ForceSpawn(evSpawningPlayer, SerpentsHandConfig, typeof(SerpentsHandConfig), PreSpawn))
if (!ClassManager.ForceSpawn(evSpawningPlayer, ClassManager.GetConfig<SerpentsHandConfig>(), typeof(SerpentsHandConfig), PreSpawn))
Logger.Error("Serpents Hand didn't spawn");
continue;
@ -280,16 +241,10 @@ public sealed class CustomClasses : Plugin
{
ev.Player.CustomInfo = "";
if (ClassManager.TryHandleSpawn(ev.Player, JanitorConfig, typeof(JanitorConfig), null)) return;
if (ClassManager.TryHandleSpawn(ev.Player, ResearchSubjectConfig, typeof(ResearchSubjectConfig), null)) return;
if (ClassManager.TryHandleSpawn(ev.Player, HeadGuardConfig, typeof(HeadGuardConfig), null)) return;
if (ClassManager.TryHandleSpawn(ev.Player, MedicConfig, typeof(MedicConfig), null)) return;
if (ClassManager.TryHandleSpawn(ev.Player, GamblerConfig, typeof(GamblerConfig), null)) return;
if (ClassManager.TryHandleSpawn(ev.Player, ShadowStepperConfig, typeof(ShadowStepperConfig), null)) return;
if (ClassManager.TryHandleSpawn(ev.Player, MtfDemolitionistConfig, typeof(MtfDemolitionistConfig), null))
if (ClassManager.Configs.Any(classManagerConfig => ClassManager.TryHandleSpawn(ev.Player, classManagerConfig.Value, classManagerConfig.Key, null)))
{
return;
if (ClassManager.TryHandleSpawn(ev.Player, ExplosiveMasterConfig, typeof(ExplosiveMasterConfig), null)) return;
if (ClassManager.TryHandleSpawn(ev.Player, BloodFueledConfig, typeof(BloodFueledConfig), null)) return;
}
}
private static void OnScp914ProcessingPickup(Scp914ProcessingPickupEventArgs ev)
@ -330,6 +285,7 @@ public class CustomClassManager
private readonly object _lock = new();
private readonly Random _random = new();
private readonly Dictionary<Type, SpawnState> _spawnStates = new();
public Dictionary<Type, CustomClassConfig> Configs { get; } = new();
/// <summary>
/// Initializes a new instance of the <see cref="CustomClassManager"/> class and registers all handlers.
@ -378,13 +334,30 @@ public class CustomClassManager
/// <param name="spawnState">Optional custom spawn state</param>
private void RegisterHandler<T>(ICustomClassHandler handler, [CanBeNull] SpawnState spawnState = null) where T : CustomClassConfig
{
T config;
try
{
config = Activator.CreateInstance<T>();
}
catch (Exception ex)
{
Logger.Error($"Failed to create config instance for {typeof(T).Name}: {ex.Message}");
return;
}
lock (_lock)
{
_spawnStates[typeof(T)] = spawnState ?? new SpawnState();
_handlers[typeof(T)] = handler;
Configs[typeof(T)] = config;
}
}
public T GetConfig<T>() where T : CustomClassConfig
{
return (T)Configs[typeof(T)];
}
/// <summary>
/// Resets all spawn states for a new round.
/// </summary>
@ -444,22 +417,13 @@ public class NegromancerShadowHandler : CustomClassHandler
{
public override void HandleSpawn(Player player, CustomClassConfig config, Random random)
{
base.HandleSpawn(player,config,random);
player.MaxHealth = 1000;
player.MaxHumeShield = 0;
player.HumeShield = 0;
player.EnableEffect<MovementBoost>(10, float.PositiveInfinity);
const string customInfo = "<color=#A0A0A0>Shadow</color>";
if (!Player.ValidateCustomInfo(customInfo, out var reason))
{
Logger.Error($"Invalid custom info for Shadow: {reason}");
}
else
{
player.CustomInfo = customInfo;
player.InfoArea |= PlayerInfoArea.CustomInfo;
}
}
}
@ -479,12 +443,32 @@ public interface ICustomClassHandler
public abstract class CustomClassHandler: ICustomClassHandler
{
public abstract void HandleSpawn(Player player, CustomClassConfig config, Random random);
public virtual void HandleSpawn(Player player, CustomClassConfig config, Random random)
{
var info = config.FullCustomInfo;
public virtual void HandleEscape(Player player, CustomClassConfig config)
if (!Player.ValidateCustomInfo(info, out var reason))
{
Logger.Error($"[{GetType().Name}] Invalid custom info: {reason}");
return;
}
player.CustomInfo = info;
player.InfoArea |= PlayerInfoArea.CustomInfo;
SendSpawnMessage(player, config);
}
protected virtual void HandleEscape(Player player, CustomClassConfig config)
{
//Intentionally left blank
}
protected virtual void SendSpawnMessage(Player player, CustomClassConfig config)
{
if (config.Name.IsEmpty()) return;
player.SendBroadcast($"You are a {config.FullCustomInfo}!", CustomClasses.BroadcastDuration);
}
}
public enum JanitorSpawn
@ -643,7 +627,7 @@ public class ShadowStepperHandler : CustomClassHandler
player.SendBroadcast("You're a <color=#000000>ShadowStepper</color>!", CustomClasses.BroadcastDuration);
}
public override void HandleEscape(Player player, CustomClassConfig config)
protected override void HandleEscape(Player player, CustomClassConfig config)
{
base.HandleEscape(player, config);
@ -664,6 +648,7 @@ public abstract class SimpleAddItemHandler : CustomClassHandler
{
public override void HandleSpawn(Player player, CustomClassConfig config, Random random)
{
base.HandleSpawn(player,config,random);
foreach (var spawnItem in config.Items)
{
player.AddItem(spawnItem, ItemAddReason.StartingItem);
@ -868,6 +853,11 @@ public abstract class CustomClassConfig
/// The required role for this class to be considered.
/// </summary>
public virtual RoleTypeId RequiredRole { get; set; } = RoleTypeId.ClassD;
public virtual string Name { get; init; } = string.Empty;
public virtual string Color { get; init; } = "#FFFFFF";
public string FullCustomInfo => $"<color={Color}>{Name}</color>";
}
/// <summary>
@ -958,9 +948,9 @@ public sealed class FlashMasterConfig : CustomClassConfig
public sealed class NegromancerConfig : CustomClassConfig
{
public override double ChancePerPlayer { get; set; } = 0.0;
public override int MaxSpawns { get; set; } = 1;
public override RoleTypeId RequiredRole { get; set; } = RoleTypeId.Scp049;
public override ItemType[] Items { get; set; } = [];
public override string Name { get; init; } = "Shadowmancer";
public override string Color { get; init; } = "#A0A0A0";
}
public sealed class NegromancerShadowConfig : CustomClassConfig
@ -968,7 +958,8 @@ public sealed class NegromancerShadowConfig : CustomClassConfig
public override double ChancePerPlayer { get; set; } = 0.0;
public override int MaxSpawns { get; set; } = int.MaxValue;
public override RoleTypeId RequiredRole { get; set; } = RoleTypeId.Scp106;
public override ItemType[] Items { get; set; } = [];
public override string Name { get; init; } = "Shadow";
public override string Color { get; init; } = "#A0A0A0";
}
public sealed class BloodFueledConfig : CustomClassConfig
@ -976,7 +967,6 @@ public sealed class BloodFueledConfig : CustomClassConfig
public override double ChancePerPlayer { get; set; } = 1.0;
public override int MaxSpawns { get; set; } = int.MaxValue;
public override RoleTypeId RequiredRole { get; set; } = RoleTypeId.Scp939;
public override ItemType[] Items { get; set; } = [];
}
/// <summary>

View File

@ -1,8 +0,0 @@
using PlayerRoles;
namespace CustomClasses;
public abstract class ExtendedClass: PlayerRoleBase
{
}

View File

@ -1,4 +1,3 @@
using System.Net;
using CustomPlayerEffects;
using LabApi.Events.Arguments.Scp049Events;
using LabApi.Events.Handlers;
@ -13,19 +12,9 @@ namespace CustomClasses;
public class NegromancerHandler : CustomClassHandler
{
public override void HandleSpawn(Player player, CustomClassConfig config, Random random)
protected override void SendSpawnMessage(Player player, CustomClassConfig config)
{
player.SendBroadcast("You are the <color=#6e2e99>Negromancer</color>! Revived players become your <color=#3c1361>Shadow</color>.", CustomClasses.BroadcastDuration);
const string customInfo = "<color=#A0A0A0>Shadowmancer</color>";
if (!Player.ValidateCustomInfo(customInfo, out var reason))
{
Logger.Error($"Invalid custom info for Negromancer: {reason}");
}
else
{
player.CustomInfo = customInfo;
player.InfoArea |= PlayerInfoArea.CustomInfo;
}
}
}
@ -153,12 +142,11 @@ public class NegromancerManager
private void OnScp049ResurrectedBody(Scp049ResurrectedBodyEventArgs ev)
{
var classManager = _plugin.ClassManager;
// Check if the reviver is a Negromancer
if (classManager == null ||
!IsNegromancer(ev.Player)) return;
ev.Target.SetRole(RoleTypeId.Scp106, RoleChangeReason.Respawn, RoleSpawnFlags.None);
classManager.ForceSpawn(ev.Target, _plugin.NegromancerShadowConfig, typeof(NegromancerShadowConfig), null);
classManager.ForceSpawn(ev.Target, classManager.GetConfig<NegromancerShadowConfig>(), typeof(NegromancerShadowConfig), null);
}
}

View File

@ -0,0 +1,11 @@
using LabApi.Features.Wrappers;
namespace CustomClasses;
public static class PlayerExtensions
{
public static string GetExtendedClass(this Player player)
{
return "";
}
}

View File

@ -10,7 +10,6 @@ using LabApi.Events.Handlers;
using LabApi.Features.Permissions;
using LabApi.Features.Wrappers;
using MEC;
using Mirror;
using PlayerRoles;
using UnityEngine;
using LightSourceToy = LabApi.Features.Wrappers.LightSourceToy;
@ -110,16 +109,6 @@ public class SerpentsHandManager
public static void PreSpawn(Player player)
{
player.SetRole(RoleTypeId.Tutorial, RoleChangeReason.RespawnMiniwave, RoleSpawnFlags.None);
const string customInfo = "<color=#32CD32>SerpentsHand</color>";
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<float> UpdateSerpentsHandHint()
@ -151,11 +140,11 @@ public class SerpentsHandManager
// ReSharper disable once IteratorNeverReturns
}
public void SpawnSerpentWave()
public static void SpawnSerpentWave()
{
var state = (SerpentsHandState)CustomClasses.Instance.ClassManager.GetSpawnState(typeof(SerpentsHandConfig));
var serpentsHandConfig = _customClasses.SerpentsHandConfig;
var serpentsHandConfig = CustomClasses.Instance.ClassManager.GetConfig<SerpentsHandConfig>();
state.SetSpawned();
state.SetWillSpawn();
@ -244,6 +233,8 @@ public sealed class SerpentsHandConfig : CustomClassConfig
public const float BaseChance = 90f;
public readonly Vector3[] SpawnLocations = [new(0.22f, 300.96f, -0.31f), new(123.921f, 288.792f, 20.929f)];
public override string Color { get; init; } = "#32CD32";
public override string Name { get; init; } = "Serpents Hand";
}
public class SerpentsHandHandler : SimpleAddItemHandler
@ -322,7 +313,7 @@ public class SpawnSerpentsCommand : ICommand
return false;
}
CustomClasses.Instance.SerpentsHandManager.SpawnSerpentWave();
SerpentsHandManager.SpawnSerpentWave();
response = "success";
return true;

View File

@ -33,10 +33,7 @@ public class SetCClassCommand : ICommand
return false;
}
// Find the last argument as the class name
var className = args[arguments.Offset + arguments.Count - 1].ToLower();
// Join all arguments except the last one to get the full player name
var playerName = string.Join(" ", args.Skip(arguments.Offset).Take(arguments.Count - 1));
var player = Player.ReadyList.FirstOrDefault(x => x.Nickname == playerName || x.UserId == playerName);
@ -46,28 +43,24 @@ public class SetCClassCommand : ICommand
return false;
}
var customClasses = CustomClasses.Instance;
var manager = new CustomClassManager();
var manager = CustomClasses.Instance.ClassManager;
var success = className switch
{
"janitor" => manager.ForceSpawn(player, customClasses.JanitorConfig, typeof(JanitorConfig), null),
"subject" or "researchsubject" => manager.ForceSpawn(player, customClasses.ResearchSubjectConfig, typeof(ResearchSubjectConfig), null),
"headguard" => manager.ForceSpawn(player, customClasses.HeadGuardConfig, typeof(HeadGuardConfig), null),
"medic" => manager.ForceSpawn(player, customClasses.MedicConfig, typeof(MedicConfig), null),
"gambler" => manager.ForceSpawn(player, customClasses.GamblerConfig, typeof(GamblerConfig), null),
"shadowstepper" => manager.ForceSpawn(player, customClasses.ShadowStepperConfig, typeof(ShadowStepperConfig), null),
"demolitionist" => manager.ForceSpawn(player, customClasses.MtfDemolitionistConfig, typeof(MtfDemolitionistConfig), null),
"scout" => manager.ForceSpawn(player, customClasses.ScoutConfig, typeof(ScoutConfig), null),
"explosivemaster" => manager.ForceSpawn(player, customClasses.ExplosiveMasterConfig, typeof(ExplosiveMasterConfig), null),
"flashmaster" => manager.ForceSpawn(player, customClasses.FlashMasterConfig, typeof(FlashMasterConfig), null),
"serpentshand" => manager.ForceSpawn(player, customClasses.SerpentsHandConfig, typeof(SerpentsHandConfig),
() =>
{
SerpentsHandManager.PreSpawn(player);
}),
"negromancer" => manager.ForceSpawn(player, customClasses.NegromancerConfig, typeof(NegromancerConfig), null),
"bloodfueled" => manager.ForceSpawn(player, customClasses.BloodFueledConfig, typeof(BloodFueledConfig), null),
"janitor" => manager.ForceSpawn(player, manager.GetConfig<JanitorConfig>(), typeof(JanitorConfig), null),
"subject" or "researchsubject" => manager.ForceSpawn(player, manager.GetConfig<ResearchSubjectConfig>(), typeof(ResearchSubjectConfig), null),
"headguard" => manager.ForceSpawn(player, manager.GetConfig<HeadGuardConfig>(), typeof(HeadGuardConfig), null),
"medic" => manager.ForceSpawn(player, manager.GetConfig<MedicConfig>(), typeof(MedicConfig), null),
"gambler" => manager.ForceSpawn(player, manager.GetConfig<GamblerConfig>(), typeof(GamblerConfig), null),
"shadowstepper" => manager.ForceSpawn(player, manager.GetConfig<ShadowStepperConfig>(), typeof(ShadowStepperConfig), null),
"demolitionist" => manager.ForceSpawn(player, manager.GetConfig<MtfDemolitionistConfig>(), typeof(MtfDemolitionistConfig), null),
"scout" => manager.ForceSpawn(player, manager.GetConfig<ScoutConfig>(), typeof(ScoutConfig), null),
"explosivemaster" => manager.ForceSpawn(player, manager.GetConfig<ExplosiveMasterConfig>(), typeof(ExplosiveMasterConfig), null),
"flashmaster" => manager.ForceSpawn(player, manager.GetConfig<FlashMasterConfig>(), typeof(FlashMasterConfig), null),
"serpentshand" => manager.ForceSpawn(player, manager.GetConfig<SerpentsHandConfig>(), typeof(SerpentsHandConfig),
() => SerpentsHandManager.PreSpawn(player)),
"negromancer" => manager.ForceSpawn(player, manager.GetConfig<NegromancerConfig>(), typeof(NegromancerConfig), null),
"bloodfueled" => manager.ForceSpawn(player, manager.GetConfig<BloodFueledConfig>(), typeof(BloodFueledConfig), null),
_ => false
};