From d3a81f5bef60ea1eae4337317bab106034c7b882 Mon Sep 17 00:00:00 2001 From: Lumi Date: Sun, 10 Aug 2025 21:58:06 +0100 Subject: [PATCH] feat: start on 049-2 changes --- SillySCP/API/Features/Scp049DataStore.cs | 42 ++++++++++++ SillySCP/Handlers/Scp049Handler.cs | 74 ++++++++++++++++++++ SillySCP/Patches/DisconnectPatch.cs | 86 ++++++++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 SillySCP/API/Features/Scp049DataStore.cs create mode 100644 SillySCP/Handlers/Scp049Handler.cs create mode 100644 SillySCP/Patches/DisconnectPatch.cs diff --git a/SillySCP/API/Features/Scp049DataStore.cs b/SillySCP/API/Features/Scp049DataStore.cs new file mode 100644 index 0000000..b076111 --- /dev/null +++ b/SillySCP/API/Features/Scp049DataStore.cs @@ -0,0 +1,42 @@ +using LabApi.Events.Arguments.PlayerEvents; +using LabApi.Events.Arguments.Scp049Events; +using LabApi.Events.Handlers; +using LabApi.Features.Stores; +using LabApi.Features.Wrappers; +using PlayerRoles; +using SecretAPI.Features; +using UnityEngine; +using Logger = LabApi.Features.Console.Logger; + +namespace SillySCP.API.Features +{ + public class LastKnownInformation + { + public Vector3 Position { get; set; } + + public float Health { get; set; } + + public float MaxHealth { get; set; } + + public float HumeShield { get; set; } + + public float MaxHumeShield { get; set; } + } + public class Scp049DataStore : CustomDataStore + { + public Scp049DataStore(Player player) + : base(player) + { + ActiveStores.Add(this); + } + + /// + /// List of IDs of people who have left which should be SCP-049-2. + /// + public Dictionary Leavers { get; } = []; + + public List ActivePlayers { get; } = []; + + public static List ActiveStores { get; } = []; + } +} \ No newline at end of file diff --git a/SillySCP/Handlers/Scp049Handler.cs b/SillySCP/Handlers/Scp049Handler.cs new file mode 100644 index 0000000..2d3d215 --- /dev/null +++ b/SillySCP/Handlers/Scp049Handler.cs @@ -0,0 +1,74 @@ +using LabApi.Events.Arguments.PlayerEvents; +using LabApi.Events.Arguments.Scp049Events; +using LabApi.Events.Handlers; +using LabApi.Features.Console; +using PlayerRoles; +using SecretAPI.Features; +using SillySCP.API.Features; + +namespace SillySCP.Handlers +{ + public class Scp049Handler : IRegister + { + public void TryRegister() + { + Scp049Events.ResurrectedBody += OnRevived; + PlayerEvents.Dying += OnDying; + PlayerEvents.Joined += OnJoined; + ServerEvents.RoundRestarted += OnRoundRestarted; + } + + public void TryUnregister() + { + Scp049Events.ResurrectedBody -= OnRevived; + PlayerEvents.Dying -= OnDying; + PlayerEvents.Joined -= OnJoined; + ServerEvents.RoundRestarted -= OnRoundRestarted; + } + + public static void OnRevived(Scp049ResurrectedBodyEventArgs ev) + { + Scp049DataStore store = ev.Player.GetDataStore(); + store.ActivePlayers.Add(ev.Target); + } + + public static void OnDying(PlayerDyingEventArgs ev) + { + if(ev.Player.Role == RoleTypeId.Scp049) + Scp049DataStore.ActiveStores.Remove(Scp049DataStore.ActiveStores.FirstOrDefault(store => store.Owner == ev.Player)); + + if (ev.Player.Role != RoleTypeId.Scp0492) + return; + + foreach (Scp049DataStore store in Scp049DataStore.ActiveStores.Where(store => store.ActivePlayers.Contains(ev.Player))) + { + store.ActivePlayers.Remove(ev.Player); + } + } + + public static void OnJoined(PlayerJoinedEventArgs ev) + { + Scp049DataStore store = Scp049DataStore.ActiveStores.FirstOrDefault(store => store.Leavers.ContainsKey(ev.Player.UserId)); + if (store == null) + return; + + store.ActivePlayers.Add(ev.Player); + + LastKnownInformation info = store.Leavers[ev.Player.UserId]; + + ev.Player.Role = RoleTypeId.Scp0492; + ev.Player.MaxHealth = info.MaxHealth; + ev.Player.Health = info.Health; + ev.Player.MaxHumeShield = info.MaxHumeShield; + ev.Player.HumeShield = info.HumeShield; + ev.Player.Position = info.Position; + + store.Leavers.Remove(ev.Player.UserId); + } + + public static void OnRoundRestarted() + { + Scp049DataStore.ActiveStores.Clear(); + } + } +} \ No newline at end of file diff --git a/SillySCP/Patches/DisconnectPatch.cs b/SillySCP/Patches/DisconnectPatch.cs new file mode 100644 index 0000000..0b487cd --- /dev/null +++ b/SillySCP/Patches/DisconnectPatch.cs @@ -0,0 +1,86 @@ +using System.Reflection.Emit; +using HarmonyLib; +using Interactables.Interobjects.DoorUtils; +using LabApi.Features.Wrappers; +using MapGeneration; +using PlayerRoles; +using PlayerStatsSystem; +using SillySCP.API.Features; +using UnityEngine; +using Logger = LabApi.Features.Console.Logger; + +namespace SillySCP.Patches +{ + [HarmonyPatch(typeof(CustomNetworkManager), nameof(CustomNetworkManager.OnServerDisconnect))] + public static class DisconnectPatch + { + public static IEnumerable Transpiler(IEnumerable instructions) + { + CodeMatcher matcher = new CodeMatcher(instructions) + .MatchStartForward( + new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(PlayerStats), nameof(PlayerStats.DealDamage))) + ) + .Advance(-6) + .Insert( + new CodeInstruction(OpCodes.Ldloc_1), + new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(DisconnectPatch), nameof(OnBeforeDeath))) + ); + + return matcher.InstructionEnumeration(); + } + + public static void OnBeforeDeath(ReferenceHub hub) + { + Player player = Player.Get(hub); + + if (player is not { Role: RoleTypeId.Scp0492 }) + return; + + Scp049DataStore store = Scp049DataStore.ActiveStores.FirstOrDefault(store => store.ActivePlayers.Contains(player)); + if (store == null) + return; + + Vector3 position; + + if (player.Room != null) + { + IEnumerable doors = player.Room.Doors.Where(door => + door is not ElevatorDoor && !door.IsLocked && door.Permissions.HasFlag(DoorPermissionFlags.None)); + + float closestDistance = float.MaxValue; + Door closestDoor = null; + foreach (Door door in doors) + { + if (Vector3.Distance(door.Position, player.Position) >= closestDistance) + continue; + closestDistance = Vector3.Distance(door.Position, player.Position); + closestDoor = door; + } + + position = closestDoor?.Position ?? player.Room.Position; + position += Vector3.one; + + if (player.Room.Name is RoomName.EzGateA or RoomName.EzGateB or RoomName.HczCheckpointToEntranceZone) + { + position = player.Position; + } + } + else + { + position = player.Position; + } + + LastKnownInformation info = new() + { + Position = position, + Health = player.Health, + MaxHealth = player.MaxHealth, + HumeShield = player.HumeShield, + MaxHumeShield = player.MaxHumeShield, + }; + + store.Leavers.Add(player.UserId, info); + store.ActivePlayers.Remove(player); + } + } +} \ No newline at end of file