various fixes + new custom class shadowmancer
This commit is contained in:
parent
0615f8aeea
commit
78fa56dce9
@ -98,7 +98,7 @@ public class AfkSwap : Plugin
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_playerPositions[playerTime.Key].Equals(playerTime.Key.Position))
|
||||
if ((_playerPositions[playerTime.Key] - playerTime.Key.Position).sqrMagnitude > 2)
|
||||
{
|
||||
_playerSpawnTimes.Remove(playerTime.Key);
|
||||
_playerPositions.Remove(playerTime.Key);
|
||||
|
@ -11,11 +11,13 @@ using LabApi.Events.Arguments.Scp914Events;
|
||||
using LabApi.Events.Arguments.ServerEvents;
|
||||
using LabApi.Events.Handlers;
|
||||
using LabApi.Features;
|
||||
using LabApi.Features.Enums;
|
||||
using LabApi.Features.Wrappers;
|
||||
using LabApi.Loader.Features.Plugins;
|
||||
using MapGeneration;
|
||||
using MEC;
|
||||
using PlayerRoles;
|
||||
using PlayerRoles.PlayableScps.Scp106;
|
||||
using Scp914.Processors;
|
||||
using UnityEngine;
|
||||
using Logger = LabApi.Features.Console.Logger;
|
||||
@ -32,6 +34,7 @@ public sealed class CustomClasses : Plugin
|
||||
{
|
||||
public readonly CustomClassManager ClassManager = new();
|
||||
public SerpentsHandManager SerpentsHandManager;
|
||||
public NegromancerManager NegromancerManager;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string Name => "CustomClasses";
|
||||
@ -77,6 +80,8 @@ public sealed class CustomClasses : Plugin
|
||||
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();
|
||||
|
||||
internal readonly Dictionary<Player, Hint> Hints = new();
|
||||
|
||||
@ -142,6 +147,7 @@ public sealed class CustomClasses : Plugin
|
||||
}
|
||||
}
|
||||
|
||||
NegromancerManager = new NegromancerManager(this);
|
||||
|
||||
Instance = this;
|
||||
}
|
||||
@ -330,6 +336,8 @@ public class CustomClassManager
|
||||
RegisterHandler<ExplosiveMasterConfig>(new ExplosiveMasterHandler());
|
||||
RegisterHandler<FlashMasterConfig>(new FlashMasterHandler());
|
||||
RegisterHandler<SerpentsHandConfig>(new SerpentsHandHandler(), new SerpentsHandState());
|
||||
RegisterHandler<NegromancerConfig>(new NegromancerHandler());
|
||||
RegisterHandler<NegromancerShadowConfig>(new NegromancerShadowHandler());
|
||||
}
|
||||
|
||||
public SpawnState GetSpawnState(Type configType)
|
||||
@ -420,6 +428,29 @@ public class CustomClassManager
|
||||
}
|
||||
}
|
||||
|
||||
public class NegromancerShadowHandler : CustomClassHandler
|
||||
{
|
||||
public override void HandleSpawn(Player player, CustomClassConfig config, Random 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for custom class spawn handlers.
|
||||
/// </summary>
|
||||
@ -444,6 +475,15 @@ public abstract class CustomClassHandler: ICustomClassHandler
|
||||
}
|
||||
}
|
||||
|
||||
public enum JanitorSpawn
|
||||
{
|
||||
Lcz173,
|
||||
Lcz914,
|
||||
LczGr18,
|
||||
Lcz330
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Handler for the Janitor custom class.
|
||||
/// </summary>
|
||||
@ -451,13 +491,51 @@ public class JanitorHandler(CustomClassManager manager) : CustomClassHandler
|
||||
{
|
||||
public override void HandleSpawn(Player player, CustomClassConfig config, Random random)
|
||||
{
|
||||
var scp914 = Map.Rooms.FirstOrDefault(r => r.Name == RoomName.Lcz914);
|
||||
if (scp914 == null)
|
||||
var spawnLocation = (JanitorSpawn)random.Next(0, 4);
|
||||
|
||||
switch (spawnLocation)
|
||||
{
|
||||
Logger.Error("LCZ 914 room not found for Janitor spawn.");
|
||||
return;
|
||||
case JanitorSpawn.Lcz914:
|
||||
var scp914 = Map.Rooms.FirstOrDefault(r => r.Name == RoomName.Lcz914);
|
||||
if (scp914 == null)
|
||||
{
|
||||
Logger.Error("LCZ 914 room not found for Janitor spawn.");
|
||||
return;
|
||||
}
|
||||
manager.TeleportPlayerToAround(player, scp914.Position);
|
||||
break;
|
||||
|
||||
case JanitorSpawn.Lcz173:
|
||||
var lcz173Door = Map.Doors.FirstOrDefault(x => x.DoorName == DoorName.Lcz173Connector);
|
||||
if (lcz173Door == null)
|
||||
{
|
||||
Logger.Error("LCZ 173 connector door not found for Janitor spawn.");
|
||||
return;
|
||||
}
|
||||
manager.TeleportPlayerToAround(player, lcz173Door.Position);
|
||||
break;
|
||||
case JanitorSpawn.LczGr18:
|
||||
var lczGr18Door = Map.Doors.FirstOrDefault(x => x.DoorName == DoorName.LczGr18Inner);
|
||||
if (lczGr18Door == null)
|
||||
{
|
||||
Logger.Error("LCZ Gr18 door not found for Janitor spawn.");
|
||||
return;
|
||||
}
|
||||
manager.TeleportPlayerToAround(player, lczGr18Door.Position);
|
||||
break;
|
||||
case JanitorSpawn.Lcz330:
|
||||
var lcz330Door = Map.Doors.FirstOrDefault(x => x.DoorName == DoorName.Lcz330);
|
||||
if (lcz330Door == null)
|
||||
{
|
||||
Logger.Error("LCZ 330 door not found for Janitor spawn.");
|
||||
return;
|
||||
}
|
||||
manager.TeleportPlayerToAround(player, lcz330Door.Position);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
manager.TeleportPlayerToAround(player, scp914.Position);
|
||||
|
||||
foreach (var spawnItem in config.Items)
|
||||
{
|
||||
player.AddItem(spawnItem, ItemAddReason.StartingItem);
|
||||
@ -865,6 +943,22 @@ public sealed class FlashMasterConfig : CustomClassConfig
|
||||
public override ItemType[] Items { get; set; } = [ItemType.GrenadeFlash];
|
||||
}
|
||||
|
||||
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 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; } = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tracks the spawn state for a custom class.
|
||||
/// </summary>
|
||||
|
164
CustomClasses/NegromancerHandler.cs
Normal file
164
CustomClasses/NegromancerHandler.cs
Normal file
@ -0,0 +1,164 @@
|
||||
using System.Net;
|
||||
using CustomPlayerEffects;
|
||||
using LabApi.Events.Arguments.Scp049Events;
|
||||
using LabApi.Events.Handlers;
|
||||
using LabApi.Features.Console;
|
||||
using LabApi.Features.Wrappers;
|
||||
using MEC;
|
||||
using PlayerRoles;
|
||||
using PlayerRoles.PlayableScps.Scp049;
|
||||
using PlayerRoles.PlayableScps.Scp106;
|
||||
|
||||
namespace CustomClasses;
|
||||
|
||||
public class NegromancerHandler : CustomClassHandler
|
||||
{
|
||||
public override void HandleSpawn(Player player, CustomClassConfig config, Random random)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class NegromancerManager
|
||||
{
|
||||
private readonly CustomClasses _plugin;
|
||||
|
||||
public static bool IsNegromancer(Player player) => player.CustomInfo.Contains("Shadowmancer");
|
||||
public static bool IsShadow(Player player) => player.CustomInfo.Contains("Shadow") && !IsNegromancer(player);
|
||||
|
||||
|
||||
public NegromancerManager(CustomClasses plugin)
|
||||
{
|
||||
_plugin = plugin;
|
||||
Scp049Events.ResurrectedBody += OnScp049ResurrectedBody;
|
||||
|
||||
Timing.RunCoroutine(HealNearbyShadows());
|
||||
|
||||
Scp106Events.TeleportingPlayer += ev =>
|
||||
{
|
||||
if (!IsShadow(ev.Player)) return;
|
||||
ev.IsAllowed = false;
|
||||
ev.Target.EnableEffect<CardiacArrest>(1, float.PositiveInfinity);
|
||||
ev.Target.Damage(40f, ev.Player);
|
||||
ev.Player.SendHitMarker();
|
||||
};
|
||||
|
||||
Scp106Events.UsingHunterAtlas += ev =>
|
||||
{
|
||||
if (!IsShadow(ev.Player)) return;
|
||||
ev.IsAllowed = false;
|
||||
|
||||
var position = ev.DestinationPosition;
|
||||
var room = Room.GetRoomAtPosition(position);
|
||||
|
||||
if (room?.LightController == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var stat = ev.Player.GetStatModule<VigorStat>();
|
||||
|
||||
stat.CurValue = 0;
|
||||
|
||||
room.LightController.FlickerLights(3);
|
||||
};
|
||||
|
||||
Scp106Events.ChangingSubmersionStatus += ev =>
|
||||
{
|
||||
if (!IsShadow(ev.Player)) return;
|
||||
ev.IsAllowed = false;
|
||||
|
||||
var stat = ev.Player.GetStatModule<VigorStat>();
|
||||
|
||||
ev.Player.DisableEffect<MovementBoost>();
|
||||
|
||||
var scaled = stat.CurValue * 40f;
|
||||
var scaledByte = (byte)scaled;
|
||||
|
||||
if(scaledByte < 15) scaledByte = 15;
|
||||
|
||||
Logger.Debug($"Scaled {stat.CurValue} to {scaledByte}");
|
||||
|
||||
|
||||
ev.Player.EnableEffect<MovementBoost>(scaledByte, 20);
|
||||
stat.CurValue = 0;
|
||||
Timing.CallDelayed(10, () =>
|
||||
{
|
||||
ev.Player.DisableEffect<MovementBoost>();
|
||||
ev.Player.EnableEffect<MovementBoost>(10, float.PositiveInfinity);
|
||||
});
|
||||
};
|
||||
|
||||
Scp106Events.ChangingVigor += ev =>
|
||||
{
|
||||
if (!IsShadow(ev.Player)) return;
|
||||
var delta = ev.Value - ev.OldValue;
|
||||
delta *= 0.5f;
|
||||
ev.Value = ev.OldValue + delta;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private static IEnumerator<float> HealNearbyShadows()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
yield return Timing.WaitForSeconds(1);
|
||||
try
|
||||
{
|
||||
Player.ReadyList.Where(IsNegromancer).Where(x =>
|
||||
{
|
||||
var scp = x.RoleBase as Scp049Role;
|
||||
if (scp == null)
|
||||
{
|
||||
Logger.Error("Negromancer has no Scp049Role");
|
||||
return false;
|
||||
}
|
||||
|
||||
scp.SubroutineModule.TryGetSubroutine(out Scp049CallAbility ability);
|
||||
|
||||
if (ability) return ability.IsMarkerShown;
|
||||
|
||||
Logger.Error("Negromancer has no Scp049CallAbility");
|
||||
return false;
|
||||
|
||||
}).ToList().ForEach(player =>
|
||||
{
|
||||
Player.ReadyList.Where(IsShadow).Where(x =>
|
||||
{
|
||||
var distance = (x.Position - player.Position).SqrMagnitudeIgnoreY();
|
||||
return distance < 64;
|
||||
}
|
||||
).ToList().ForEach(x => x.Heal(10));
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
// ReSharper disable once IteratorNeverReturns
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
@ -47,6 +47,12 @@ public class SerpentsHandManager
|
||||
}
|
||||
}
|
||||
|
||||
if (Warhead.IsDetonationInProgress || Warhead.IsDetonated)
|
||||
{
|
||||
hadItem = true;
|
||||
state.Points += 1;
|
||||
}
|
||||
|
||||
if (!hadItem) return;
|
||||
|
||||
ev.Player.SendBroadcast("You brought back the SCP items...", 5);
|
||||
@ -107,6 +113,8 @@ public class SerpentsHandManager
|
||||
while (true)
|
||||
{
|
||||
yield return Timing.WaitForSeconds(1);
|
||||
|
||||
RoundSummary.singleton.ExtraTargets = Player.ReadyList.Count(IsSerpentsHand);
|
||||
|
||||
if (_customClasses.ClassManager.GetSpawnState(typeof(SerpentsHandConfig)) is not SerpentsHandState state) continue;
|
||||
|
||||
@ -134,10 +142,10 @@ public class SerpentsHandManager
|
||||
|
||||
public sealed record SerpentsHandState: SpawnState
|
||||
{
|
||||
public bool HasSpawned => _hasSpawned || PanicDisable;
|
||||
public bool HasSpawned => _hasSpawned || PanicDisable || Warhead.IsDetonated;
|
||||
public float ExtraChance;
|
||||
public int Points;
|
||||
public bool WillSpawn => _willSpawn && !PanicDisable;
|
||||
public bool WillSpawn => _willSpawn && !PanicDisable && !Warhead.IsDetonated;
|
||||
|
||||
private bool _hasSpawned;
|
||||
private bool _willSpawn;
|
||||
|
@ -1,9 +1,11 @@
|
||||
using CommandSystem;
|
||||
using LabApi.Features.Permissions;
|
||||
using LabApi.Features.Wrappers;
|
||||
|
||||
namespace CustomClasses;
|
||||
|
||||
[CommandHandler(typeof(RemoteAdminCommandHandler))]
|
||||
[CommandHandler(typeof(ClientCommandHandler))]
|
||||
public class SetCClassCommand : ICommand
|
||||
{
|
||||
public string Command => "setcclass";
|
||||
@ -11,6 +13,19 @@ public class SetCClassCommand : ICommand
|
||||
public string Description => "Forces a player to become a specific custom class";
|
||||
public bool Execute(ArraySegment<string> arguments, ICommandSender sender, out string response)
|
||||
{
|
||||
var executor = Player.Get(sender);
|
||||
if (executor == null)
|
||||
{
|
||||
response = "You must be a player to use this command!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!executor.HasPermissions("customclasses.setcustomclass"))
|
||||
{
|
||||
response = "You do not have permission to use this command!";
|
||||
return false;
|
||||
}
|
||||
|
||||
var args = arguments.Array!;
|
||||
if (arguments.Count < 2)
|
||||
{
|
||||
@ -51,6 +66,7 @@ public class SetCClassCommand : ICommand
|
||||
{
|
||||
SerpentsHandManager.PreSpawn(player);
|
||||
}),
|
||||
"negromancer" => manager.ForceSpawn(player, customClasses.NegromancerConfig, typeof(NegromancerConfig), null),
|
||||
_ => false
|
||||
};
|
||||
|
||||
|
@ -46,6 +46,11 @@ public class ScpSwap : Plugin
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev.Role.RoleTypeId == RoleTypeId.Scp0492)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ev.Player.SendBroadcast("Willst du dein SCP wechseln? Drücke Ö und gebe .scpswap <SCP-NUMMER> ein.", 10);
|
||||
};
|
||||
}
|
||||
|
@ -33,6 +33,12 @@ public class SwapCommand : ICommand
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player.Role == RoleTypeId.Scp0492)
|
||||
{
|
||||
response = "You can't swap SCPs while you're a zombie!";
|
||||
return false;
|
||||
}
|
||||
|
||||
List<string> validScp =
|
||||
[
|
||||
"049",
|
||||
|
42
VIPTreatment/BullshitDetectedCommand.cs
Normal file
42
VIPTreatment/BullshitDetectedCommand.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using CommandSystem;
|
||||
using LabApi.Features.Permissions;
|
||||
using LabApi.Features.Wrappers;
|
||||
|
||||
namespace VIPTreatment;
|
||||
|
||||
[CommandHandler(typeof(RemoteAdminCommandHandler))]
|
||||
[CommandHandler(typeof(ClientCommandHandler))]
|
||||
public class BullshitDetectedCommand : ICommand
|
||||
{
|
||||
public string Command => "bullshitdetected";
|
||||
|
||||
public string[] Aliases => [];
|
||||
|
||||
public string Description => "Broadcasts a message to all players";
|
||||
|
||||
public bool Execute(ArraySegment<string> 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("viptreatment.wiki"))
|
||||
{
|
||||
response = "You must have the permission to use this command!";
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var target in Player.ReadyList)
|
||||
{
|
||||
target.SendBroadcast("<color=red>⚠️ BULLSHIT DETECTED ⚠️</color>", 5);
|
||||
target.SendBroadcast("<color=green>Tipp: Das Wiki hat immer die aktuellsten Informationen!</color>", 5);
|
||||
}
|
||||
|
||||
Cassie.Message("false information yield_0.8 detected yield_01 see V yield_0.2 KEY", true, false, false);
|
||||
|
||||
response = "Broadcast message sent to all ready players.";
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using PlayerRoles;
|
||||
using LabApi.Features;
|
||||
using LabApi.Features.Wrappers;
|
||||
|
||||
namespace VisibleSpectators;
|
||||
@ -42,6 +41,12 @@ public static class PlayerDisplayUtil
|
||||
{ "TEAL", "008080" },
|
||||
{ "GOLD", "EFC01A" }
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, (string Color, string Name)> PlayerSpecificDisplays = new()
|
||||
{
|
||||
{ "76561198372750067@steam", ("DEAFCC", "1+1=10") },
|
||||
{ "76561198372587687@steam", ("9933FF", "HoherGeist") }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Returns a formatted display string for a player, with color.
|
||||
@ -49,6 +54,12 @@ public static class PlayerDisplayUtil
|
||||
public static string PlayerToDisplay(Player player)
|
||||
{
|
||||
if (player is not { IsReady: true }) return string.Empty;
|
||||
|
||||
if (PlayerSpecificDisplays.TryGetValue(player.UserId, out var specificDisplay))
|
||||
{
|
||||
return $"<color=#{specificDisplay.Color}>{specificDisplay.Name}</color>";
|
||||
}
|
||||
|
||||
const string defaultColor = "FFFFFF";
|
||||
try
|
||||
{
|
||||
|
2
build.sh
2
build.sh
@ -12,7 +12,7 @@ excluded_projects=("TemplateProject" "RangeBan" "LobbyGame" "RangeBan.Tests" "St
|
||||
|
||||
# Find project directories (containing .csproj files)
|
||||
echo "Copying DLLs to output folder..."
|
||||
for proj in $(find . -name "*.csproj"); do
|
||||
find . -path "./fuchsbau" -prune -o -path "./testbau" -prune -o -name "*.csproj" -print0 | while IFS= read -r -d '' proj; do
|
||||
# Extract project name from .csproj file
|
||||
proj_name=$(basename "$proj" .csproj)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user